一 Visual VM
1.1 概述
Visual VM是一个功能强大的多合一故障诊断和性能监控的可视化工具。
它集成了多个JDK命令行工具,使用Visual VM可用于显示虚拟机进程及进程的配置和环境信息(jps,jinfo),监视应用程序的CPU、GC、堆、方法区及线程的信息(jstat、jstack)等,甚至代替JConsole。
VisualVM: Home
1.在idea中进行安装:
2.功能结构说明:
1.2 概述
二 VisualGC功能介绍
2.1 visualGC的安装
如果页面缺少visual GC 插件,则需要选择【工具 -》 插件】进行安装,安装插件:
安装成功后,重启一下visualvm,就可以看到菜单栏上多出一个Visual GC插件:
2.2 功能页面介绍
Visual gc 工具分成布局分成三部分,可在右上角对应方框里勾选【Space】【Graphs】【Histogram】:
可视化GC窗口(space):space(Metaspace(元数据)、Old老年代、新生代(Eden、S0、S1))
图形统计窗口(Graphs):Graphs(Compile Time(编译时间)、Class Loader Time(类加载时间)、GC Time(垃圾收集时间)、Eden Space、Survivor 0、Survivor 1、Old Gen、Metaspace)
幸存者年龄直方图窗口(Histogram):Histogram(Parameters参数设置)
2.2.1 可视化GC窗口(space)
VisualGC窗口是最左的窗口,分成三条垂直柱体,在JDK1.8版本中,分别代表metaSpace元空间、Old老年代、新生代,其中新生代又划分成 Eden 区, S0 区, S1区三部分。柱体里颜色部分代表占用的空间,空白部分表示剩余空间。监控项目的堆进程时,这些代表颜色的地方都是动态变化的。
2.2.2 图形统计窗口(Graphs)
1.compile Time
功能: 显示将Java字节代码编译为本机代码所花费的时间量。窄脉冲表示持续时间相对较短,宽脉冲表示持续时间较长。
编译任务的数量: 1101 compiles(1101次编译)
累计编译时间: 27.721s。
2.class loader Time
此面板显示在类加载和卸载活动中花费的时间量。窄脉冲表示持续时间相对较短,宽脉冲表示持续时间较长。
加载的类数量:1058;
卸载的类的数量:63
累计的类加载时间:278.915s
3. GC Time:
此面板显示垃圾收集活动所花费的时间量。窄脉冲表示持续时间相对较短,宽脉冲表示持续时间较长。
执行GC垃圾回收总次数:42次(42collections代表自监视以来执行2次GC,其中,包括新生代的Minor GC和老年代的Full Gc)
累计的GC时间: 140.179ms;
若JVM维护hotspot.gc.cause和hotspot.gc.last_cause计数器,则gc事件的原因将出现在last Cause中;
4.Eden space
此面板显示Eden空间随时间的利用情况。它是年轻代的三个空间之一,另外两个分别是S0、S1。空间的当前容量可以根据收集器策略动态更改,即通过修改–Xmn参数,会改变其大小。
标题栏第一个参数代表最大容量,第二个参数代表当前容量,后跟当前占用空间。此外,还包含了年轻代GC事件数量和GC累计时间。
Eden Space最大可分配空间:3.807G;
Eden Space当前已分配空间:210M;
Eden Space当前占用空间:162M(当积累的占用空间超过162M,就会在Eden Space发生一次Minor GC)
Minor GC次数:41次
Minor GC花费时间:618.824ms
5.Survivor 0 and Survivor 1
HotSpot JVM把年轻代分为了三部分:1个Eden区和2个Survivor区(分别叫from和to),默认大小比例为Eden:Survivor0:Survivor1=8:1:1的。
新创建的非大对象,会存放在Eden区和一个作为from的Survivor区,当发生一次Minor GC时,就会将Eden区和作为from的Survivor区内仍存活的对象,复制到另一个作为to的Survivor区,然后清理掉原来Eden区和作为from的Survivor区内对象。因此,S0 和 S1 之间至少有一个肯定是空闲的。
Survivor 1区最大分配容量:3.807G;
Survivor 1区当前已分配容量:20M;
Survivor 1区当前占用容量:20M;
6.old Gen
面板显示老年代随着时间推移的利用情况。
Old Gen 最大分配空间3.807G;
Old Gen 已分配空间2.654G;
Old Gen 当前占用空间1.964G;
Old Gen 发生的GC次数:0次;
Old Gen 发生的GC花费时间:0ms;
7.MetaSpace元空间内容
标题栏在括号中显示空间的名称及其最大容量和当前容量,后跟空间的当前占用大小
最大元空间:1.062G
可用元空间: 6.688M
以占用元空间: 6.436M
https://blog.csdn.net/qq_41158114/article/details/137642072
三 VisualGC实操分析案例
3.1 案例分析
使用visual VM工具的Visual GC插件观察到以下的图表——新生代Eden区已经发生了8168次Minor GC,耗时39.754s,另外老年代也发生了24次GC,耗时5.124s。可见,该JVM参数设置得极不合理,导致已经过于频繁发生Minor GC。(除代码问题)
截图中,可以看到新生代中的Eden区频繁发生Minor GC,原因之一是分配的空间过小,目前是204.875M,导致当前占用空间经常超过204.875M,进而发生GC。若要分析是哪些代码频繁创建对象,还得进一步通过dump等方式进行分析,这里暂时不展开。
这里采用解决思路:提升分配给Eden区的大小。
通过Visual VM的监视栏中的堆监控,可以观察到蓝色模块高度比较均衡地对应在纵坐标280MB的样子,也就是说,新创建的对象+每次gc未被清除的对象 其占有的大小达到近300MB( 见下图蓝色模块 ),而Eden Space + 其中一个Survivor才230MB左右(见上图,Eden区 204.875+25.562约等于230M),可见,每次新创建的对象很容易就超过新生代,这就意味着,频繁发生Minor GC是必然的,从图的横坐标可以看出,每30ms内,就发生了2到3次的Minor GC
优化实施:
为了避免Eden区频繁发生Minor GC,根据堆监控图表,可以考虑在设置JVM参数时,适当提升分配给Eden的空间。
就暂且先设置Eden区为320MB,考虑到Eden:Survivor0:Survivor1=8:1:1比例,也就是8:2,若要分配Eden=320M,那么,可以根据8/2=320/x算出来,x=80,这里的x就是两个Survivor总大小,即每个Survivor分配40MB,那么,年轻代总共需要分配的大小为(320M+40M*2)=400M,即-Xmn400m。
再来看下老年代,目前老年代发生了24次GC,最大分配空间是256MB,当前最小分配空间是71.48M,可见,还可以适当进行优化。
根据堆=新生代+老年代,不包括永久代(方法区)。在新生代已经分配400MB情况下,若要让老年代最大与最小分配空间都为256MB,那么,就需要对JVM堆分配400M+256M=656M的空间大小,即设置-Xms656M、-Xmx656M;
元空间暂且可以不考虑进行分配。
最后,根据以上得出的参数,进行设置,然后以设置好的参数进行项目重启,根据新一轮图表展示,继续进行参数优化,循环调试,直到新生代和老年代的GC频率都保持一个比较平衡的水准。
3.2 获取堆文件的两种方式
1.设置参数在异常发生时自动生成dump文件。
-XX:+HeapDumpOnOutOfMemoryError 表示当JVM发生OOM时,自动生成DUMP文件。
-XX:HeapDumpPath=存储文件/目录 表示生成DUMP文件的路径
2.手动生成dump分析文件
执行jmap -dump:format=b,file=20210321.dump 7132,其中7132是对应项目的进程PID。
将获取到的dump文件手动导入到Visual VM工具,就可以分析哪些对象占用内存高了,往往可以分析出哪些对象造成了内存泄露问。