不知道大家知不道自己项目中的JVM相关参数,比如设置的堆的内存是多少,新生代内存多少,什么垃圾回收器,GC频率等等,所以这篇文章从实际出发探索项目中的JVM参数。
1.准备工作
内存分析工具准备:
有很多内存分析工具,如IBM的HeapAnalyzer,eclipse的MAT
这里使用eclipse的MAT
官方下载地址
常用命令:
top 查看监控整体信息
jps 可以查看部署的Java Pid
jinfo 可以查看jvm相关参数
jstat 可以查看gc相关信息,比如gc次数,新生代,老年代占比等等
jmap 用于查看堆信息,当然也可以导出dump文件
这些都是命令行工具,当然也有可视化工具
jconsole,jvisualvm
2.直接开始
由于我们生产环境不允许操作,所以退而求次使用测试环境来操作。
首先我是发现我们young gc比较频繁,大概1分多钟一次;full gc次数比较低。触发young gc的条件就是eden区满了,所以想知道为啥eden区这么快就满了。可以看到YGC次数已经1061次了
参数含义在下边再解释
①使用top或者jps命令找到Java进程的Pid
top -b 找到java pid 35
jps 35
②使用jinfo查看我们的jvm信息,可以看到新生代,老年代,垃圾回收器类型等等
jinfo -flags 35
我们一般用下列参数来初始化jvm参数
-Xms 最小堆内存
-Xmx 最大堆内存
-Xmn 新生代内存=-XX:newSize = -XX:MaxnewSize = -Xmn.也就是说-Xmn会同时设置前面2个参数
-XX:+UseXXGC 表示使用哪种GC
-XX:PermSize 设置永久代最小空间大小。
-XX:MaxPermSize 设置永久代最大空间大小。
-Xss 设置每个线程的堆栈大小。
-XX:NewRatio=n:设置年轻代和年老代的比值。如:为3,表示年轻代与年老代比值为1:3,年轻代占整个年轻代年老代和的1/4
-XX:SurvivorRatio=n:年轻代中Eden区与两个Survivor区的比值。注意Survivor区有两个。如:3,表示Eden:Survivor=3:2,一个Survivor区占整个年轻代的1/5
③使用jstat查看gc信息
jstat -gcutil 35 以百分比展示堆信息
各参数含义:
S0 survivor区
S1 survivor区
E eden区
O 老年代区
M jdk1.8是元空间 1.7是P 永久代
CCS 压缩空间
YGC young gc 次数
YGCT young gc 耗时
FGC full gc 次数
FGCT full gc 耗时
GCT gc总耗时
可以参考这个
④使用jmap导出堆栈信息
jmap -dump:format=b,file=heap.hprof 35
⑤打开刚刚下载的MAT工具,左上角file将jmap导出的heap.hprof文件导入MAT工具
这里我们主要关注 leak suspects,很贴心,已经把可能发生内存泄露的地方给我们找出来了,点进去看看。
可以看到上面26.47%的内存都是我打码的对象,这个类是全链路的监控的类,用于产生拦截对象,可以猜测一下,全链路我们是用Java探针接入的,类似于代理,在我们请求前后使用拦截器拦截收集我们的请求信息,然后请求完了,就可以回收拦截器对象了,所以可以看到我们eden区由于创建拦截器对象很快就慢了,然后又被ygc回收掉。
当然实际排查过程中没有这么简单,通常隐藏层级很深,这是就要用到其他特性了。
比如Dominator Tree: 可以看到对象所占内存占比,我们就可以优先排查那些大对象
可以看到上图的Shallow Heap 和 Retained Heap
Shallow Heap 和 Retained Heap的含义:
Shallow Heap:是对象本身占据的内存的大小,不包含其引用的对象
Retained Heap:当前对象大小+当前对象可直接或间接引用到的对象的大小总和。(间接引用的含义:A->B->C,C就是间接引用) ,并且排除被GC Roots直接或者间接引用的对象
具体区别可以参考这篇文章:
添加链接描述
而这个对象被gc回收释放的内存就是Retained Heap
然后点击对象进行 点击list objetcs incoming 和 outgoing references分析
incoming references:引用该对象的对象
outgoing references:该对象引用的对象
比如A,B引用C,C引用D,E
站在C的角度,AB就是C的incoming references,DE是C的outgoing references
我们这是查看的outgoing references,也就是该对象引用的对象,可以看到有一个array,里面全是拦截器对象,可以印证我们刚刚上面说的
大体流程就是这样,大家不懂多搜,多尝试。