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的状态,而且是对同一个方法,同一段代码一直执行占用。这个时候就是很大的机率是对该该方法存在死循环了。就可以对该进行杀死。但是这样杀死程序对程序破坏程度损伤很大的。为了更好提早排除这种情况,个人觉得建议循环里面可以添加填日志,即使死循环也可以提早预知和处理,减少重大事故的发生。
(由于能力和时间有限,暂时分析到这里,如有不对,希望大家多多怼出来,日后一定好好改正)