最近在做项目的时候,到了最后准备上线的时候,通过不断的压力测试,发现,在压测某个功能的时候发现内存占用在不断的上涨,而且还不会被回收;在这种情况下,如果并发量增大的话,必然会导致内存泄露的。
通过看代码,以及工具分析,最后找到了原因所在,是因为:定义了静态的ThreadLocal变量,里面存放了一个MAP,而这个MAP里面的key是一个Object
出问题的就是这个MAP,静态的ThreadLocal存放在JVM的方法区(他用于存在已被虚拟机加载的类信息、常量、静态变量等数据,另外方法区也叫永久代),垃圾收集行为在这个区域比较少出现,但并非数据进入了方法区就如永久代的名字一样“永久”存在了。一般来说,这个区域的回收“成绩”比较难以令人满意。
下面说一下JVM调优的几种方案以供参考:
(1)最简单的就是看代码,不管是否自己写的代码,从头开始去梳理一遍,主要针对数组,List,MAP这种对象进行重点检查,这种数据结构中一直持有对象不释放,很容易就会造成内存泄露。
(2)代码分析完了之后如果还未发现问题(确实,如果只是看代码有的时候确实很难看出问题),就可以通过把dump文件打出来通过visualvm进行分析,里面的对象引用关系都能找到,我们就是通过这种方式找到问题。
打出dump内容的方法:输入命令jmap -dump:format=b,file=test.dump 8799(进程ID)
dump文件创建完成之后,通过visualvm进行打开分析即可。
其他的命令行工具:jstat,虚拟机统计信息监视工具,可以用来统计年轻代,老年代的内存占用和回收情况等;命令:jstat -gc 8799 1000 10(每1000毫秒查询一次进程为8799垃圾收集的情况,一共收集10次)
(3)可视化工具:VisualVm和Jprofiler,我觉得这两个工具非常强大,我个人觉得Jprofiler比较给力,通过Jprofiler也能分析出上面我们遇到的问题,里面的对象引用关系看的一清二楚,具体使用哪个,视情况而定。这两个可视化工具的具体使用,网上有很多的参考资料,我们可以找到很详细的说明,在此就不赘述了。
本文探讨了在使用静态ThreadLocal变量存放MAP时,由于MAP中的key为Object类型,导致内存占用持续增加且无法被回收,最终引发内存泄露的问题。文中提供了JVM调优的几种方案,包括代码审查、使用命令行工具(如jmap、jstat)和可视化工具(如VisualVm、Jprofiler)来定位问题,并提出了具体的解决步骤。
488

被折叠的 条评论
为什么被折叠?



