写点什么

visualvm 工具远程对 linux 服务器上的 JVM 虚拟机进行监控与调优

用户头像
朱季谦
关注
发布于: 2021 年 03 月 26 日

文/朱季谦

 

最近做了一些 JVM 监控与调优的事情,算是第一次实践,还比较陌生,故而先把这一次经验简单记下笔记,这样,对后面学习调优方面时,不至于又想不起来了。

本文档主要总结在 window 本地环境远程对 linux 服务断的 JVM 虚拟机进行监控与调优的方法。

visualvm 工具是 JDK 自带的,在 java 安装目录下可以找到:C:\Program Files\Java\jdk1.8.0_77\bin



打开 visualvm 工具,右击远程,添加远程主机——



在弹出框上的主机名处,填写需要连接的服务器 IP——



添加成功后,右边框就出现了以下图标——



这时,右击“42.194.xx.xx”,会看到,有两种远程连接方式,一个是 JMX,一个是 jstatd。

这里主要分享是以 jstatd 模式。在以 jstatd 模式连接前,需要在监控的远程服务端启动 jstatd,启动步骤如下——

1.找到服务端 jdk 的 bin 目录,新建 jstatd.all.policy 文件

[root@VM-16-4-centos bin]# whereis javajava: /usr/bin/java [root@VM-16-4-centos bin]# cd /usr/bin/[root@VM-16-4-centos bin]# vim jstatd.all.policy
复制代码

将以下内容复制到 jstatd.all.policy 文件里——

grant codebase "file:${java.home}/../lib/tools.jar" {  permission java.security.AllPermission;};
复制代码

保存,设置权限——

[root@VM-16-4-centos bin]# chmod 777 jstatd.all.policy
复制代码

设置成功——


2.在监控的远程服务端启动 jstatd

执行 jstatd -J-Djava.security.policy=jstatd.all.policy -J-Djava.rmi.server.hostname=42.194.xxx.xx &

注:42.194.xxx.xx 是我个人腾讯云机器 IP。

[root@VM-16-4-centos bin]#  jstatd -J-Djava.security.policy=jstatd.all.policy -J-Djava.rmi.server.hostname=42.194.xxx.xx &[1] 52056
复制代码

这时,观察 visualvm 工具右边栏菜单,可以看到远程连接的服务端已经自动连接上 jstatd,这就意味着,可以在本地通过远程 jstatd 来监控开发服务器上的 jvm 信息了,从而进行 jvm 调优监控等操作。

点击其中一个进程 biz-0.0.1-SNZPSHOT.jar,就可以进入到对应的监控台——


visualvm 控制台有概述、监视、抽样器等菜单,同时,可以装入插件扩展功能——

  • 概述

可以看到 jvm 参数、系统属性、jdk 版本与安装路径等信息;

设置的 jvm 参数,这里新生代分配了最小堆空间是 256m,最大堆空间是 256m,新生代 128m,元空间是 128m,堆=新生代+老年代,不包括永久代(方法区),这就意味着,这配置当中的老年代=256m。

-Xms256M -Xmx256M -Xmn128M -XX:PermSize=128M -XX:MaxPermSize=256M

这些都是指定 JVM 参数运行对应的 jar 进程,除此之外,还有其他参数可做设置。

  • 监视

监控模块可以实时看到进程所在的堆、元空间、类及线程的报表数据监控,其中,堆和元空间的报表对调优可以起到很有用的帮助。



除了 visualvm 自带的功能外,我们需要装入一个实时监控 GC 的插件 visualgc,这个插件很方便对 JVM 做监控与调优。

考虑到 visualgc 插件通过官网下载很慢,我已经保存在网盘当中,可直接通过网盘进行下载——

链接:https://pan.baidu.com/s/17TSf0ZdFtMdfog6xzbj3ZQ 提取码:el6c

插件装载方式,右击工具栏,选择插件——



弹出框后,点“已下载”按钮,再点击“添加插件”将需要安装的 visualgc 插件添加进来——



安装成功后,重启一下 visualvm,就可以看到菜单栏上多出一个 Visual GC 插件——


Visual gc 工具分成布局分成三部分,可在右上角对应方框里勾选【Space】【Graphs】【Histogram】,它们各自的作用——

  • 可视化 GC 窗口(space)

  • 图形统计窗口(Graphs)

  • 幸存者年龄直方图窗口(Histogram)

下面分别介绍各自窗口与其显示的数字表示——

  1. 可视化 GC 窗口(space)

  2. VisualGC 窗口是最左的窗口,分成三条垂直柱体,在 JDK1.8 版本中,分别代表 metaSpace 元空间、Old 老年代、新生代,其中新生代又划分成 Eden 区, S0 区, S1 区三部分。柱体里颜色部分代表占用的空间,空白部分表示剩余空间。监控项目的堆进程时,这些代表颜色的地方都是动态变化的。

图形统计窗口(Graphs)

图形窗口显示各种统计值随时间的变化。

一、Compile Time

显示将 Java 字节代码编译为本机代码所花费的时间量。窄脉冲表示持续时间相对较短,宽脉冲表示持续时间较长。

  • 编译任务的数量 5508;

  • 累计编译时间 27.721s。



二、Class Loader Time

此面板显示在类加载和卸载活动中花费的时间量。窄脉冲表示持续时间相对较短,宽脉冲表示持续时间较长。

  • 加载的类数量:11337;

  • 卸载的类的数量:0

  • 累计的类加载时间:15.589s

三、GC Time

此面板显示垃圾收集活动所花费的时间量。窄脉冲表示持续时间相对较短,宽脉冲表示持续时间较长。

  • 执行 GC 垃圾回收总次数:9 次(9 collections 代表自监视以来执行 9 次 GC,其中,包括新生代的 Minor GC 和老年代的 Full Gc)

  • 累计的 GC 时间:888.929ms;

  • 若 JVM 维护 hotspot.gc.cause 和 hotspot.gc.last_cause 计数器,则 gc 事件的原因将出现在 last Cause 中;


四、Eden Space

此面板显示 Eden 空间随时间的利用情况。它是年轻代的三个空间之一,另外两个分别是 S0、S1。空间的当前容量可以根据收集器策略动态更改,即通过修改--Xmn 参数,会改变其大小。

标题栏第一个参数代表最大容量,第二个参数代表当前容量,后跟当前占用空间。此外,还包含了年轻代 GC 事件数量和 GC 累计时间。

  • Eden Space 最大可分配空间:102.500M;

  • Eden Space 当前已分配空间:102.500M;

  • Eden Space 当前占用空间:54.523M(当积累的占用空间超过 102.500M,就会在 Eden Space 发生一次 Minor GC)

  • Minor GC 次数:6 次

  • Minor GC 花费时间:286.621ms


五、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 0 区最大分配容量:12.750M;

  • Survivor 0 区当前已分配容量:12.750M;

  • Survivor 0 区当前占用容量:0M;

五、Old Gen

面板显示老年代随着时间推移的利用情况。

  • Old Gen 最大分配空间 128M;

  • Old Gen 已分配空间 128M;

  • Old Gen 当前占用空间 38.06M;

  • Old Gen 发生的 GC 次数:3 次;

  • Old Gen 发生的 GC 花费时间:602.309ms;


六、Perm Gen

标题栏在括号中显示空间的名称及其最大容量和当前容量,后跟空间的当前占用大小。


visual VM 工具的相关功能使用主要就介绍那么多,下面就介绍一下入门调优的案例,小白都能看懂的。

假如某天你观察到使用 visual VM 工具的 Visual GC 插件观察到以下的图表——新生代 Eden 区已经发生了 8168 次 Minor GC,耗时 39.754s,另外老年代也发生了 24 次 GC,耗时 5.124s。可见,该 JVM 参数设置得极不合理,导致已经过于频繁发生 Minor GC。



那么,我们该如何调优进行设置呢?

JVM 调优无外乎就是对相关参数进行设置,这里,我们先做一些最简单的参数,好让小白也能理解,那么,就暂时先对-Xms、-Xmx、-Xmn 参数设置。

截图中,可以看到新生代中的 Eden 区频繁发生 Minor GC,原因之一是分配的空间过小,目前是 204.875M,导致当前占用空间经常超过 204.875M,进而发生 GC。若要分析是哪些代码频繁创建对象,还得进一步通过 dump 等方式进行分析,这里暂时不展开。

解决该 Eden 区其中一个思路是,提升分配给 Eden 区的大小。

那么,多大才比较合适呢?

这时,Visual VM 的监视栏中的堆监控就派上用场了。可以观察到蓝色模块高度比较均衡地对应在纵坐标 240MB 的样子,也就是说,新创建的对象其占有的大小达到近 300MB,而 Eden Space+其中一个 Survivor 才 230MB,可见,每次新创建的对象很容易就超过新生代,这就意味着,频繁发生 Minor GC 是必然的,从图的横坐标可以看出,每 30ms 内,就发生了 2 到 3 次的 Minor GC。



为了避免 Eden 区频繁发生 Minor GC,根据堆监控图表,可以考虑在设置 JVM 参数时,适当提升分配给 Eden 的空间,至少需要在 240MB 以上,可以考虑先设置到 300MB 的样子,看下效果,当然,这是在项目比较平稳运行的情况下来看的,实际生产当中,还需要考虑到高峰时期。

就暂且先设置 Eden 区为 320MB,考虑到 Eden:Survivor0:Survivor1=8:1:1 比例,也就是 8:2,若要分配 Eden=320M,那么,可以根据 8/2=320/x 算出来,x=80,这里的 x 就是两个 Survivor 总大小,即每个 Survivor 分配 40MB,那么,年轻代总共需要分配的大小为(320M+80M)=400M,即-Xmn400m

再来看下老年代,目前老年代发生了 24 次 GC,最大分配空间是 256MB,当前最小分配空间是 71.48M,可见,还可以适当进行优化。



一般而言,最大分配空间与最小分配空间最好保持一致,这样避免每次空间不够时都需自动提升当前分配大小。

可以暂且考虑最大分配空间与当前分配空间都保持在 256M,而根据堆=新生代+老年代,不包括永久代(方法区)。在新生代已经分配 300MB 情况下,若要让老年代最大与最小分配空间都为 256MB,那么,就需要对 JVM 堆分配 400M+256M=656M 的空间大小,即设置-Xms656M、-Xmx656M;

元空间暂且可以不考虑进行分配。

根据以上得出的参数,进行设置,然后以设置好的参数进行项目重启,根据新一轮图表展示,继续进行参数优化,循环调试,直到新生代和老年代的 GC 频率都保持一个比较平衡的水准。

以上,就是主要介绍了 JVM 监控与调优工具,同时,简单说明了一下如何进行参数调优,实际上,还需调试更多 JVM 相关参数,才能达到优化效果,至于其他的 JVM 参数调试,本文暂且不展开介绍了。

最后,需要注意一点,本地环境使用 jstatd 模式远程连接线上服务端的 JVM 时,是不能在本地获取到堆栈信息的,可以手动生成 dump 文件来分析出现异常的堆栈信息。

一、设置参数在异常发生时自动生成 dump 文件。

  • -XX:+HeapDumpOnOutOfMemoryError 表示当 JVM 发生 OOM 时,自动生成 DUMP 文件。

  • -XX:HeapDumpPath=存储文件/目录 表示生成 DUMP 文件的路径

二、手动生成 dump 分析文件

执行 jmap -dump:format=b,file=20210321.dump 7132,其中 7132 是对应项目的进程 PID。

将获取到的 dump 文件手动导入到 Visual VM 工具,就可以分析哪些对象占用内存高了,往往可以分析出哪些对象造成了内存泄露问题。


发布于: 2021 年 03 月 26 日阅读数: 7
用户头像

朱季谦

关注

一个以编程为生的荒诞小说家 2018.06.29 加入

汤圆创作APP签约作者; 简书优秀认证作者; Java后端程序员; PMP认证人员; 学生时代著有《黑色玫瑰》一书已上架微信读书APP;

评论

发布
暂无评论
visualvm工具远程对linux服务器上的JVM虚拟机进行监控与调优