jstat:查看JVM统计信息
jstat(JVM Statistics Monitoring Tool):用于监视虚拟机各种运行状态信息的命令行工具。它可以显示本地或者远程虚拟机进程中的类装载、内存、垃圾收集、JIT编译等运行数据。在没有GUI图形界面,只提供了纯文本控制台环境的服务器上,它将是运行期定位虚拟机性能问题的首选工具。常用于检测垃圾回收问题以及内存泄漏问题。
官方文档:https://docs.oracle.com/javase/8/docs/technotes/tools/unix/jstat.html
基本使用语法为:jstat - [-t] [-h] [ []]
查看命令相关参数:jstat-h 或 jstat-help
其中vmid是进程id号,也就是jps之后看到的前面的号码,如下:
我们看到上面的指令语法,那些中括号括起来的都是可以不加可以加的。其中-option有多种格式。下面来一一看一下。
option参数
选项option可以由以下值构成。
类装载相关的:
● -class:显示ClassLoader的相关信息:类的装载、卸载数量、总空间、类装载所消耗的时间等
垃圾回收相关的:
● -gc:显示与GC相关的堆信息。包括Eden区、两个Survivor区、老年代、永久代等的容量、已用空间、GC时间合计等信息。
● -gccapacity:显示内容与-gc基本相同,但输出主要关注Java堆各个区域使用到的最大、最小空间。
● -gcutil:显示内容与-gc基本相同,但输出主要关注已使用空间占总空间的百分比。
● -gccause:与-gcutil功能一样,但是会额外输出导致最后一次或当前正在发生的GC产生的原因。
● -gcnew:显示新生代GC状况
● -gcnewcapacity:显示内容与-gcnew基本相同,输出主要关注使用到的最大、最小空间
● -geold:显示老年代GC状况
● -gcoldcapacity:显示内容与-gcold基本相同,输出主要关注使用到的最大、最小空间
● -gcpermcapacity:显示永久代使用到的最大、最小空间。
JIT相关的:
● -compiler:显示JIT编译器编译过的方法、耗时等信息
● -printcompilation:输出已经被JIT编译的方法
因为option有这么多,但是其余的种类是不多的,所以我们只取一个option的参数和其余的进行搭配,先把其余的拿下,再回来看option剩下的,我们就选择class进行操作。
1、jstat -class vmid
我们指定一下进程id,进行分析他的class信息,我们看一下输出。
1、先看一下我的程序进程id是51048
然后执行jstat -class 51048
我们看到输出的信息如上图,他妈的水印太垃圾了,分别有这么几列
2、jstat -class vmid interval
上面我们输出了一行数据,意思就是他采集了一次当前信息。假如我们想周期性的输出采集结果呢,比如我们想一秒采集一次,而不是就采集一次就完了。就可以指定这个interval的参数,翻译一下也是周期的意思。
我们执行jstat -class 51048 1000这个指令即可,1000的意思是1000毫秒,也就是一秒输出一次,不断的采集。
但是呢,这个采集方式你指定了一秒一次,他就是不断的输出,一直不停,直到你的程序寄了。比如oom了。有时候我们想只采集5次就够了,那就可以指定interval后面的count参数,指定执行的次数。
3、jstat -class vmid interval count
我们执行jstat -class 51048 1000 5我们指定采集5次。我们看到了他就是输出了5次就完了。
4、jstat -class -t vmid interval count
这个-t是会在前面加一列,意思是一个时间,time的意思,这个时间代表你的程序从启动到现在经过了多少秒。
我们执行一下jstat -class -t 51048 1000 5
我们看到前面有多了一列,就是你程序的执行时间。
5、jstat -class -t -h lines vmid interval count
这个-h的意思就是表头的意思,head后面那个lines就是几行就输出一个表头,假如我们指定为2我们看看啥效果。
还有个规则,我们看一下这个完整的语句,
jstat -class -t -h lines vmid interval count
我们看到有的参数是有形参指定的比如-t 直接就是-t就知道是输出时间列的
比如-h后面加行数,这个行数代表的就是你-h的这个几行打一次表头。
但是后面的那种vmid interval count这个是没有前面的类似-h这种形参的,这就不好区分有的时候,比如你输入了一个jstat -class -t -h 2 51048 5
这时候后面这个5就是5毫秒输出一次,打印的刷刷的很快,不是代表的一共输出5次了,这里有个顺序问题,需要注意一下。
下面的这些option参数和-class一样,也可以和后面那些参数一起使用。
jstat -compiler
我们执行一下jstat -compiler 线程id,我们先看一下程序的线程id是54400
接下来我们用jstat采集信息。
以上采集结果的输出各行分别代表的意思是:
jstat -printcompilation
我们看到编译了28个结构,大小是5,编译的具体方法就是get方法,这里还需要处理一下TODO
-gc
显示与GC相关的堆信息。包括Eden区、两个Survivor区、老年代、永久代等的容量、已用空间、GC时间合计等信息。
我们执行一下这个jstat -gc 1556 1000 5表示监控一下1556号线程的gc信息,没秒采集一次,一共采集五次。
我们依次说一下这几列的表头含义。
S0C:表示当前你这个程序启动时候设置的s0区的总大小。
S1C:表示当前你这个程序启动时候设置的s1区的总大小。c我理解就是个count的意思。
S0U:表示当前你这个程序采集的时候s0区已经使用了的大小。
S1U:表示当前你这个程序采集时候s1区已经使用了的大小,U我理解就是use使用的意思。
EC:表示当前你这个程序采集的时候eden区设置的大小。
EU:表示当前你这个程序采集时候eden区已经使用了的大小.。
OC:表示当前你这个程序采集的时候老年代设置的总大小。
OU:表示当前你这个程序采集时候老年代已经使用了的大小。
MC:表示当前你这个程序采集的时候方法区设置的总大小。
MU:表示当前你这个程序采集时候方法区已经使用了的大小。
CCSC:表示当前你这个程序采集的时候压缩类设置的总容量。
CCSU:表示当前你这个程序采集的时候压缩类已经使用的容量。
YGC:采集的时候youngGC发生的次数。
YGCT:采集的时候youngGC一共占用的时间。
FGC:采集的时候fullGC发生的次数。
FGCT:采集的时候fullGC一共占用的时间。
GCT:采集的时候一共GC的时间,也就是FGCT+YGCT
接下来我们就来关注一下GC的一个监控,但是呢上面的程序不太能反应gc变化,我们新开一个程序。
/**
* @author: levi
* @description: -Xms60m -Xmx60m -XX:SurvivorRatio=8 堆空间初始和最大值设置为60m,s区和eden区的比例为8:1:1
* @date: 2022-10-3 20:10
* @version: 1.0
*/
public class GCtest {
// 100KB大小
static final int _100KB = 1024 * 100;
public static void main(String[] args) {
List<byte[]> list = new ArrayList<>();
for (int i = 0; i < 1000; i++) {
// 每120毫秒就添加100KB进这个集合里面
byte[] arr = new byte[_100KB];
list.add(arr);
try {
Thread.sleep(120);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
-gc参数
启动程序,jps查看进程号后,开启监控。
我们先分析一下,我们看到s0和s1区的总大小都是2048字节,也就是2m。我大体分析一下。
我们设置堆空间是60m。默认的新生代和老年代的比例是1:2可以通过newRatio来控制。
也就是说新生代目前是占了三分之一,也就是20M,而eden区和s区的比例是8:1:1,所以s区每个占十分之一,也就是2m,也就是2048kb,可见是没啥问题的。
OK,这个-gc参数就是这么个意思,每个参数的意思都是如上所述。
jstat -gcutil 显示内容与-gc基本相同,但输出主要关注已使用空间占总空间的百分比。
我们执行一下jstat -gcutil 98920 1000 80 每秒采集一次,一共采集80次。
他的这几列基本和-gc差不多,他是-gc的一个子集,他只反应占比例的哪几项。
s0表示s0区的当前占用比例
其余的也都是占比,后面的还是时间长度。
jstat -gccapacity
jstat -gccause
jstat -gcnew
jstat -gcnewcapacity
jstat -gcold
jstat -gcoldcapacity
以上命令上面都有解释了,可以具体测试一下。
1、关于gc时间的一个使用
我们每次采集的时候可以在-gc这类参数后面加上-t,每次输出你当前程序已经执行的时间,我们可以比较两次时间之间的GCT的时间,把这些GCT加起来,也就是你这个时间段里面GC一共耗费的时间,要是高于了百分之90那就说明你的程序可能随时要OOM要寄了。要是高于百分之20就说明有压力了,可以适当的针对性调优一下。
2、关于内存泄露
jstat还可以用来判断是否出现内存泄漏。
第1步:在长时间运行的 Java 程序中,我们可以运行jstat命令连续获取多行性能数据,并取这几行数据中 OU 列(即已占用的老年代内存)的最小值。
第2步:然后,我们每隔一段较长的时间重复一次上述操作(取多组数据,采样更加离散有参考性),来获得多组 OU 最小值。如果这些值呈上涨趋势,则说明该 Java 程序的老年代内存已使用量在不断上涨,这意味着无法回收的对象在不断增加,因此很有可能存在内存泄漏。