[转帖]记录一次spring-boot程序内存泄露排查

记录,一次,spring,boot,程序,内存,泄露,排查 · 浏览次数 : 0

小编点评

## Spring Boot 项目 JMX 配置问题排查 **问题:** * spring boot 项目的 `Xms4g` 和 `Xmx4g` 配置参数在程序运行过程中被设置为 `Xmx2048m` 和 `Xmx1024m`,而程序最终占用了 12 个多 G 的内存,只能临时重启服务。 **解决方案:** 1. **分析内存泄漏原因:** * 使用 `jstat -class PIDjstat -compiler PIDjstat -gc PIDjstat -gccapacity PIDjstat -gcutil PID` 等工具分析堆栈信息,定位程序最占内存的类和实例。 * 使用 `jmap -dump:live,format=b,file=/tmp/m.hprof PID` 等工具下载程序的内存文件,进行内存分析。 2. **优化内存分配:** * 确定程序真正需要哪些内存空间。 * 使用 `@EnableMemoryAnalysis` 等注解配置 `MemoryAnalysis` 注解,引导 Spring Boot 在运行过程中自动分析内存。 * 设置 `spring.boot.starter.web.tomcat.max-threads` 等参数,降低线程数,减少内存占用。 3. **调整启动参数:** * 在 `application.properties` 文件中设置以下参数: * `spring.boot.starter.web.tomcat.max-connections=100` * `spring.boot.starter.web.server.tomcat.max-threads=200` * 减少 `Xmx` 参数,降低应用程序启动所需的内存。 4. **其他优化:** * 使用 `undertow` 容器替换 `tomcat` 容器,更低内存使用。 * 考虑使用 `Spring Boot Actuator` 等工具监控应用程序内存使用,及时发现和解决内存泄漏问题。 **总结:** 通过分析内存泄漏原因,优化内存分配策略,调整启动参数,以及使用其他优化方法,可以有效降低 Spring Boot 项目的内存消耗,解决 “堆外内存泄漏”问题。

正文

现象

spring boot项目jvm启动配置-Xms4g -Xmx4g,然而很不幸的是程序所占的内存越来越高,都达到了12个多G,只能临时重启服务

常用命令

  • jstat -class PID
  • jstat -compiler PID
  • jstat -gc PID
  • jstat -gccapacity PID
  • jstat -gcutil PID 查看堆比例
  • jstat -gccause PID
  • jstat -gcnew PID
  • jstat -gcnewcapacity PID
  • jstat -gcold PID
  • jstat -gcoldcapcacity PID
  • jstat -printcompilation PID
  • jmap -histo PID 查看类的实例
  • jmap -heap PID 查看堆栈信息
  • jmap -dump:live,format=b,file=/tmp/m.hprof PID 保存内存的堆栈为文件
  • jhat -J-Xmx2048m -port 5000 /tmp/m.hprof 在线查看堆文件的类,速度比较慢
  • jcmd PID GC.run 强制gc

排查一:开发环境和测试环境调试

用jdk自带的jvisualvm.exe,查看最占空间的类和实例最多的类,找到其最近的内存释放点一般就是内存泄露对象,也可以用jmap查看jvm进程实例最多的类

  1. 本机启动程序,postman或者jmeter调用程序接口,可以直接直接用jvisualvm查看到堆栈信息
  2. 远程调试,在测试环境启用jmxremote,-Dcom.sun.management.jmxremote.port=10096 -Dcom.sun.management.jmxremote.ssl=false -Dcom.sun.management.jmxremote.authenticate=false
    但是堆栈信息只能先保存在测试服务器上,下载到本地后再用jvisualvm.exe打开

排查二:保存生成环境的内存进行分析

先用jmap命令将程序的内存数据保存下来,jmap -dump:live,format=b,file=m.hprof PID,其中PID是程序的进程号

/data/server/jdk/jdk1.8.0_171/bin/jmap -dump:live,format=b,file=/tmp/m.hprof 3478
    • 将/tmp/m.hprof堆栈文件下载到本地电脑,用jvisualvm.exe打开分析,生产服务器外网太小,下载好慢
    • mat分析,我是在一台空闲一点的生成服务器上使用,阿里云内网传输文件速度特别快,将下载的mat直接解压缩就可以使用了,因为文件太大内存不足所以需要修改 MemoryAnalyzer.ini中的参数-Xmx4096M
      cd /data/tools/mat
      ./ParseHeapDump.sh /tmp/m.hprof org.eclipse.mat.api:suspects org.eclipse.mat.api:overview org.eclipse.mat.api:top_components
      程序执行完以后会在/tmp下面生成一堆文件,其中 m_System_Overview.zip ,m_Top_Components.zip,m_Leak_Suspects.zip三个压缩文件就是报告了,可以看到程序的运行概况,最大的对象,和推测泄露点
      在这里插入图片描述
      从我的内存泄露报告中可以看到Global对象groovy.lang.MetaClassImpl对象是两个主要的内存泄露点

    解决

    1)复查程序发现将groovy脚本封装到了一个ThreadLocal中,在一次请求中可以被重复利用,该ThreadLocal包括了Global对象,在函数处理结束后释放掉ThreadLocal对象,主要内存泄漏点得到修复
    2)groovy.lang.MetaClassImpl泄露解决,先升级groovy-all的版本
    发现很多的实例其实已经没有引用了,但是内存得不到释放,当系统内存不足的时候才会被释放掉
    最终解决:

    • spring-boot升级到2.1.1.RELEASE
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-parent</artifactId>
        <version>2.1.1.RELEASE</version>
        <type>pom</type>
    </dependency>
    
      • 废用tomcat容器,改用undertow当做容器
             <dependency>
      			<groupId>org.springframework.boot</groupId>
      			<artifactId>spring-boot-starter-web</artifactId>
      			<exclusions>
      				<exclusion>
      					<groupId>org.springframework.boot</groupId>
      					<artifactId>spring-boot-starter-tomcat</artifactId>
      				</exclusion>
      			</exclusions>
      		</dependency>
      		<dependency>
      			<groupId>org.springframework.boot</groupId>
      			<artifactId>spring-boot-starter-undertow</artifactId>
      		</dependency>
      

        3)java启动参数的顺序调整
        生产服务器上有些spring-boot工程的-Xmx生效,有些没有生效,并且所有的jar包jvm参数全部放到了jar包的后面
        经过测试如果在本地电脑把参数放在jar包的后面是无效的
        正确的写法: java -jar -Xmx2048m -Xms1024m test.jar --spring.profiles.active=prod

        好的内存排查文章

        Spring Boot引起的“堆外内存泄漏”排查及经验总结

        文章知识点与官方知识档案匹配,可进一步学习相关知识
        Java技能树首页概览104410 人正在系统学习中

        与[转帖]记录一次spring-boot程序内存泄露排查相似的内容:

        [转帖]记录一次spring-boot程序内存泄露排查

        现象 spring boot项目jvm启动配置-Xms4g -Xmx4g,然而很不幸的是程序所占的内存越来越高,都达到了12个多G,只能临时重启服务 常用命令 jstat -class PIDjstat -compiler PIDjstat -gc PIDjstat -gccapacity PIDj

        [转帖]调试springboot数据库系统应用时常用debug日志配置, 解决问题缩小范围时常用

        https://www.yihaomen.com/article/1853.html 摘要: 用 spring boot 开发应用时,在遇到麻烦问题时,经常会打开debug日志,下面记录一个通用的思路,基于spring boot以及jpa来实现, 一般来说,将root的日志级别设置为log.leve

        [转帖]SpringBoot配置SSL 坑点总结【密码验证失败、连接不安全】

        文章目录 前言1.证书绑定问题2.证书和密码不匹配3.yaml配置文件问题3.1 解密类型和证书类型是相关的3.2 配置文件参数混淆 后记 前言 在SpringBoot服务中配置ssl,无非就是下载证书设置一下配置文件的问题,这里主要记录我在配置的过程中遇到的坑点。 如果是新手上道的话建议结合其他的

        [转帖]记录一次前端内存泄漏排查经历

        https://juejin.cn/post/6844904019983335438 对于前端的“内存泄漏”这个东西,说实话我只在书上看到过: 闭包、匿名函数和事件绑定尤其容易造成内存泄漏。 然而这些操作造成的“内存泄漏”究竟是什么样子的?如何排查?虽然很好奇,却不得而知。直到这次公司应用频繁出现浏

        [转帖]人大金仓数据库的备份与还原

        人大金仓数据库的备份与还原 文章目录 人大金仓数据库的备份与还原前言备份sys_dump 命令 还原ksql 命令sys_restore 一. 从人大金仓数据库备份还原到人大金仓数据库二 从postgresql数据库备份还原到人大金仓数据库 后记 前言 本文记录一次使用人大金仓数据库(Kingbas

        [转帖]Redis 持久化原理和实现

        https://juejin.cn/post/6877763937513766919 Redis 所有的数据和状态存储在内存中,为了避免进程退出而导致数据丢失,需要将数据和状态保存到硬盘上。 为了达到这一目的,通常有两种实现方式: 将 Redis 当作一个状态机,记录每一次的对 Redis 的操作,

        [转帖]ASH、AWR、ADDM区别联系

        概念知识梳理 >>根据时段区间生成ASH报告: >>ASH报告信息: ASH (Active Session History) ASH以V$SESSION为基础,每秒采样一次,记录活动会话等待的事件。不活动的会话不会采样,采样工作由新引入的后台进程MMNL来完成。 ASH buffers 的最小值为

        [转帖]一次海光物理机资源竞争压测的记录

        一次海光物理机资源竞争压测的记录 https://plantegg.github.io/2021/03/07/%E4%B8%80%E6%AC%A1%E6%B5%B7%E5%85%89%E7%89%A9%E7%90%86%E6%9C%BA%E8%B5%84%E6%BA%90%E7%AB%9E%E4%B

        [转帖]一次海光物理机资源竞争压测的记录

        一次海光物理机资源竞争压测的记录 https://plantegg.github.io/2021/03/07/%E4%B8%80%E6%AC%A1%E6%B5%B7%E5%85%89%E7%89%A9%E7%90%86%E6%9C%BA%E8%B5%84%E6%BA%90%E7%AB%9E%E4%B

        [转帖]一次操作系统报错OutOfMemory Error的处理记录

        在启动公司内嵌的tomcat容器时出现报错, 如下: # There is insufficient memory for the Java Runtime Environment to continue.# Native memory allocation (malloc) failed to a