浅谈Java虚拟机的监控及诊断工具

1.基本命令的应用

1.1 jps(源操作文档

在默认情况下,jps的输出信息包括 Java 进程的进程 ID 以及主类名。我们还可以通过追加参数,来打印额外的信息。如果某 Java 进程关闭了默认开启的UsePerfData参数(即使用参数-XX:-UsePerfData),那么jps命令(以及下面介绍的jstat)将无法探知该 Java 进程。

常用的参数:

-l :将打印模块名以及包名;

-v :将打印传递给 Java 虚拟机的参数(如-XX:+UnlockExperimentalVMOptions -XX:+UseZGC);

-m:将打印传递给主类的参数。

1.2 jstat(源操作文档

jstat 命令可用来打印目标 Java 进程的性能数据。

-class : 将打印类加载相关的数据。

-compiler和-printcompilation : 将打印即时编译相关的数据。

-gc : 为前缀的子命令,它们将打印垃圾回收相关的数据。

​ 例如命令:jstat -gc 22126 1s 4 意思是每一秒打印一次 打印四次

1.3 jmap(源操作文档

jmap命令,分析 Java 虚拟机堆中的对象。jmap同样包括多条子命令。

-clstats,该子命令将打印被加载类的信息。

-finalizerinfo,该子命令将打印所有待 finalize 的对象。

-histo,该子命令将统计各个类的实例数目以及占用内存,并按照内存使用量从多至少的顺序排列。此外,

-histo:live只统计堆中的存活对象。

-dump,该子命令将导出 Java 虚拟机堆的快照。live只保存堆中的存活对象。我们通常会利用jma

1.4 jinfo(源操作文档

jinfo命令(帮助文档)可用来查看目标 Java 进程的参数。

-X :输出Java 虚拟机中的 jvm_args

-XX : 参数(即输出中的 VM Flags),以及可在 Java 层面通过System.getProperty获取的-D参数(即输出中的 System Properties)。

1.5 jstack(源操作文档

jstack命令(帮助文档)可以用来打印目标 Java 进程中各个线程的栈轨迹,以及这些线程所持有的锁。jstack的其中一个应用场景便是死锁检测。这里我用jstack获取一个已经死锁了的 Java 程序的栈信息。我们可以看到,jstack不仅会打印线程的栈轨迹、线程状态(BLOCKED)、持有的锁(locked …)以及正在请求的锁(waiting to lock …),而且还会分析出具体的死锁。

1.6 jcmd(源操作文档

可以直接使用jcmd命令,来替代前面除了jstat之外的所有命令。至于jstat的功能,虽然jcmd复制了jstat的部分代码,并支持通过PerfCounter.print子命令来打印所有的 Performance Counter,但是它没有保留jstat的输出格式,也没有重复打印的功能。

(以上内容是对极客时间的jvm的学习内容进行学习笔记,如侵删)

2.自己的实践

2.1 背景

以下我举个死模拟一个死循环的程序,然后在系统中对该程序进行监控排除。首先我们是无法感知,这个程序是否存在有死循环的。然后我们对程序进行监控和排查。

图片

2.1 对程序监控

1.先输入jps查看运行的服务上的java程序。

图片

2.利用命令jstat -gc 6388 1s 20 查看程序的虚拟机运行情况

先对-gc参数的打印出来的参数基本了解:

S0C:第一个幸存区的大小
S1C:第二个幸存区的大小
S0U:第一个幸存区的使用大小
S1U:第二个幸存区的使用大小
EC:伊甸园区的大小
EU:伊甸园区的使用大小
OC:老年代大小
OU:老年代使用大小
MC:方法区大小
MU:方法区使用大小
CCSC:压缩类空间大小

图片

以上用了jstat -gc xxxxx 5s 100命令查看jvm性能情况

可以看上图绿色是对jvm的性能数据进行监控,其中主要包括EU,OU,YGC。

首先看绿色那组数据:

看gc的第20次到第21次gc时候,s1u 清空了 s0u增大了,由于gc时候将s1u存活的对象和edn区晋升的对象复制到了s0u区,然后将s1u的对象清空了。

然后看黄色和红色的数据:

可以看到OU的数据基本增大很多了,而su区的数据减少了,因为期间进行了一次full gc将su区的对象晋升为了老生代。

看到以上的数据只是能看到jvm里面基本的内部运行情况的,还不能看出是否异常,但是对于自己熟悉的程序来说是可以看出的基本的端倪的。因为短短的时间内一直再gc,接下来继续观察一下。

图片

然后再看用同样的命令:

首先观察OU和OC的值,OU不断增大,而且值已经很接近OC了,表示程序很快会抛出OutOfMemoryError了,这是非常严重的错误的。所以一定要留意jvm的这两个值之间的比例,按照合理的情况来说,一个程序持久运行OU的应该是持久比较稳定按照一个上下波动的,但是可以看到该程序,OU的值一直再增加,而且增速异常,程序必定有bug。根据教导内容说,通常OU占OC的20%以下,超过20%就必须要预警,已经存在OutOfMemoryError的端倪了。

首先短短的30s内又进行了一次full gc ,按这个增速计算再30s,程序就会奔溃了。事实上真的奔溃了。

既然我们已经对程序运行有问题了,我们接下来是要对程序分析。

利用Jstack -l PID >>文件位置,可以将业务一个进程的所有线程的运行情况输出。

输出之后可以看到有一个nid线程是业务代码而且是一直Runable,表示一直在占用cpu无法释放。所以很可能就是这个位置出现问题,接着就是自己对代码的熟悉程度进行排查,debug等操作了。

图片

最后利用kill命令对进程杀死。

1.kill -3 pid可以打印当前进程的线程信息,但是不会关闭Java应用!

2.kill pid 也就是kill -15 pid ,将会调用钩子函数ShutdownHook,一般ShutdownHook中会进行一些操作,比如保存数据,关闭连接等。

3.kill -9 pid.不会调用钩子函数ShutdownHook

3.个人思考总结:

最常见的在项目上,出现死循环时候,会对相处一直处于占用的情况没有释放,如果是web系统常见的请求里面的线程,普通的请求,而且该请求预计是执行很快,不是耗时任务的,会发现多个线程一直处理Runable或者wait的状态,而且是对同一个方法,同一段代码一直执行占用。这个时候就是很大的机率是对该该方法存在死循环了。就可以对该进行杀死。但是这样杀死程序对程序破坏程度损伤很大的。为了更好提早排除这种情况,个人觉得建议循环里面可以添加填日志,即使死循环也可以提早预知和处理,减少重大事故的发生。

(由于能力和时间有限,暂时分析到这里,如有不对,希望大家多多怼出来,日后一定好好改正)

猜你喜欢

转载自blog.csdn.net/vipshop_fin_dev/article/details/112462623