一次完整的JVM堆外内存泄漏故障排查记录( 四 )

输出的信息中,看得出老年代和新生代都蛮正常的,元空间也只占用了20M,直接内存看起来也是2g...
嗯?为什么MaxMetaspaceSize = 17592186044415 MB?「看起来就和没限制一样」 。
再仔细看看我们的启动参数:
-Xms4g -Xmx4g -Xmn2g -Xss1024K -XX:PermSize=256m -XX:MaxPermSize=512m -XX:ParallelGCThreads=20 -XX:+UseConcMarkSweepGC -XX:+UseParNewGC -XX:+UseCMSCompactAtFullCollection -XX:CMSInitiatingOccupancyFraction=80配置的是-XX:PermSize=256m -XX:MaxPermSize=512m,也就是永久代的内存空间 。「而1.8后,Hotspot虚拟机已经移除了永久代,使用了元空间代替 。」 由于我们线上使用的是JDK1.8,「所以我们对于元空间的最大容量根本就没有做限制」,-XX:PermSize=256m -XX:MaxPermSize=512m 这两个参数对于1.8就是过期的参数 。
下面的图描述了从1.7到1.8,永久代的变更:

一次完整的JVM堆外内存泄漏故障排查记录

文章插图
 
「那会不会是元空间内存泄露了呢?」
我选择了在本地进行测试,方便更改参数,也方便使用JVisualVM工具直观的看出内存变化 。
使用JVisualVM观察进程运行首先限制住元空间,使用参数-XX:MetaspaceSize=64m -XX:MaxMetaspaceSize=128m,然后在本地循环调用出问题的接口 。
得到如图:
一次完整的JVM堆外内存泄漏故障排查记录

文章插图
 
「可以看出,在元空间耗尽时,系统出发了Full GC,元空间内存得到回收,并且卸载了很多类 。」
然后我们将元空间限制去掉,也就是使用之前出问题的参数:
-Xms4g -Xmx4g -Xmn2g -Xss1024K -XX:ParallelGCThreads=20 -XX:+UseConcMarkSweepGC -XX:+UseParNewGC -XX:+UseCMSCompactAtFullCollection -XX:CMSInitiatingOccupancyFraction=80 -XX:MaxDirectMemorySize=2g -XX:+UnlockDiagnosticVMOptions得到如图:
一次完整的JVM堆外内存泄漏故障排查记录

文章插图
 
「可以看出,元空间在不断上涨,并且已装入的类随着调用量的增加也在不断上涨,呈现正相关趋势 。」
柳暗花明又一村问题一下子明朗了起来,「随着每次接口的调用,极有可能是某个类都在不断的被创建,占用了元空间的内存」 。
观察JVM类加载情况 -verbose
?
在调试程序时,有时需要查看程序加载的类、内存回收情况、调用的本地接口等 。这时候就需要-verbose命令 。在myeclipse可以通过右键设置(如下),也可以在命令行输入java -verbose来查看 。
?
-verbose:class 查看类加载情况-verbose:gc 查看虚拟机中内存回收情况-verbose:jni 查看本地方法调用的情况我们在本地环境,添加启动参数-verbose:class循环调用接口 。
可以看到生成了无数com.alibaba.fastjson.serializer.ASMSerializer_1_WlkCustomerDto:
[Loaded com.alibaba.fastjson.serializer.ASMSerializer_1_WlkCustomerDto from file:/C:/Users/yangzhendong01/.m2/repository/com/alibaba/fastjson/1.2.71/fastjson-1.2.71.jar][Loaded com.alibaba.fastjson.serializer.ASMSerializer_1_WlkCustomerDto from file:/C:/Users/yangzhendong01/.m2/repository/com/alibaba/fastjson/1.2.71/fastjson-1.2.71.jar][Loaded com.alibaba.fastjson.serializer.ASMSerializer_1_WlkCustomerDto from file:/C:/Users/yangzhendong01/.m2/repository/com/alibaba/fastjson/1.2.71/fastjson-1.2.71.jar][Loaded com.alibaba.fastjson.serializer.ASMSerializer_1_WlkCustomerDto from file:/C:/Users/yangzhendong01/.m2/repository/com/alibaba/fastjson/1.2.71/fastjson-1.2.71.jar][Loaded com.alibaba.fastjson.serializer.ASMSerializer_1_WlkCustomerDto from file:/C:/Users/yangzhendong01/.m2/repository/com/alibaba/fastjson/1.2.71/fastjson-1.2.71.jar][Loaded com.alibaba.fastjson.serializer.ASMSerializer_1_WlkCustomerDto from file:/C:/Users/yangzhendong01/.m2/repository/com/alibaba/fastjson/1.2.71/fastjson-1.2.71.jar]当调用了很多次,积攒了一定的类时,我们手动执行Full GC,进行类加载器的回收,我们发现大量的fastjson相关类被回收 。
一次完整的JVM堆外内存泄漏故障排查记录

文章插图
 
「如果在回收前,使用jmap查看类加载情况,同样也可以发现大量的fastjson相关类:」
jmap -clstats 7984
一次完整的JVM堆外内存泄漏故障排查记录

文章插图
 
这下有了方向,「这次仔细排查代码」,查看代码逻辑里哪里用到了fastjson,发现了如下代码:


推荐阅读