【JVM】三十二、跟随jstat深入JVM内部,确保系统稳定运行!

本文将使用一款工具来分析正在运行的系统,通过这些数据分析,我们可以更好地理解系统的内存管理情况,为优化系统性能提供有力的依据

1、前文回顾

前面的文章,我们通过分析垃圾回收日志的方式,重新梳理了JVM的运行机制。我们重点探讨了对象在Eden区优先分配的原理,触发Young GC的条件和执行过程,以及对象何时被转移到老年代,还有触发Full GC的时机和其执行过程。通过对GC日志的分析,相信大家对JVM的运行原理有了更深入和透彻的理解。

接下来,我们将使用一款工具来分析正在运行的系统。我们将关注以下方面的数据:

  1. 对象增长速率:观察系统中对象创建的速度。
  2. Young GC触发频率:记录Young GC发生的次数。
  3. Young GC耗时:测量每次Young GC所需的时间。
  4. Young GC后存活对象数量:统计每次Young GC后,有多少对象没有被回收。
  5. Young GC后进入老年代的对象数量:记录每次Young GC后,有多少对象被转移到老年代。
  6. 老年代对象增长速率:监测老年代中对象的增长速度。
  7. Full GC触发频率:记录Full GC发生的次数。
  8. Full GC耗时:测量每次Full GC所需的时间。

通过这些数据分析,我们可以更好地理解系统的内存管理情况,为优化系统性能提供有力的依据。

2、功能强大的jstat

在我们日常的系统运维中,若需对运行中的JVM进行监控,以检查其整体运行状况,jstat工具便是一个极为实用的选择。

jstat工具能够让我们轻松地查看当前运行中的系统的JVM内部情况,包括Eden区、Survivor区以及老年代的内存使用情况,同时,它还能提供Young GC和Full GC的执行次数以及耗时等关键信息。

通过这些详细的指标,我们可以方便地分析出当前系统的运行状态,判断系统的内存使用压力以及垃圾回收(GC)的压力,进而评估内存分配是否合理。接下来,我们将详细探讨jstat工具的使用方法。

3、jstat -gc PID

首先,我们需要在生产环境的Linux机器上找到Java进程的PID(进程ID)。可以通过搜索引擎查找相关信息,或者直接使用jps命令来获取。

然后,我们需要针对这个Java进程执行jstat -gc PID命令。这将帮助我们查看Java进程(实际上是一个JVM)的内存和垃圾回收(GC)情况。

运行这个命令之后会看到如下列,给大家解释一下:

S0C:这是From Survivor区的大小
S1C:这是To Survivor区的大小
S0U:这是From Survivor区当前使用的内存大小
S1U:这是To Survivor区当前使用的内存大小
EC:这是Eden区的大小
EU:这是Eden区当前使用的内存大小
OC:这是老年代的大小
OU:这是老年代当前使用的内存大小
MC:这是方法区(永久代、元数据区)的大小
MU:这是方法区(永久代、元数据区)的当前使用的内存大小
YGC:这是系统运行迄今为止的Young GC次数
YGCT:这是Young GC的耗时
FGC:这是系统运行迄今为止的Full GC次数
FGCT:这是Full GC的耗时
GCT:这是所有GC的总耗时

不知道大家是否已经有所发现,实际上,这些指标都是非常实用的JVM GC分析指标。下面,我们将逐步向大家介绍如何使用这个工具。

4、其他的jstat命令

除了上面提到的jstat -gc命令,它是最常用的一个命令,用于监控Java虚拟机的垃圾回收情况。此外,还有其他一些命令可以提供更详细的信息。如下所示:

jstat -gccapacity PID:堆内存分析
jstat -gcnew PID:年轻代GC分析,这里的TT和MTT可以看到对象在年轻代存活的年龄和存活的最大年龄
jstat -gcnewcapacity PID:年轻代内存分析
jstat -gcold PID:老年代GC分析
jstat -gcoldcapacity PID:老年代内存分析
jstat -gcmetacapacity PID:元数据区内存分析

5、到底该如何使用jstat工具?

接下来,我将向大家介绍一些使用jstat工具的小技巧。首先,我们需要明确的是,当我们分析线上的JVM进程时,我们最关心哪些信息?

这些信息包括:

  1. 新生代对象增长的速率
  2. Young GC的触发频率
  3. Young GC的耗时
  4. 每次Young GC后有多少对象是存活下来的
  5. 每次Young GC过后有多少对象进入了老年代
  6. 老年代对象增长的速率
  7. Full GC的触发频率
  8. Full GC的耗时

只要掌握了这些信息,我们就可以结合之前几周的文章分析过的JVM GC优化的方法,合理分配内存空间,尽可能让对象留在年轻代不进入老年代,避免发生频繁的Full GC。这就是对JVM最好的性能优化了!因此,我们将逐步分析,通过jstat工具如何获取上述信息。

6、新生代对象增长的速率

如果你已经仔细阅读了前面的文章,你应该知道,对于JVM,首先需要了解的是随着系统的运行,每秒钟在年轻代的Eden区会分配多少对象。

要分析这个问题,你需要在线上Linux机器上运行以下命令:jstat -gc PID 1000 10。这个命令的含义是每隔1秒钟更新一行最新的jstat统计信息,总共执行10次jstat统计。

通过这个命令,你可以灵活地对线上机器以固定频率输出统计信息,观察一段时间内JVM中Eden区对象占用的变化。

例如,执行这个命令后,第一秒显示Eden区使用了200MB内存,第二秒显示的统计信息中,发现Eden区使用了205MB内存,第三秒显示的统计信息中,发现Eden区使用了209MB内存,依此类推。

这样,你就可以轻松推断出系统大约每秒钟会新增5MB左右的对象。

此外,你还可以根据自己系统的实际情况灵活调整使用。例如,如果系统负载较低,不一定每秒都有请求,那么可以将上述的1秒钟调整为1分钟,甚至10分钟,以查看系统每隔1分钟或10分钟大概增长多少对象。

一般来说,系统都有高峰和日常两种状态。例如,系统高峰期用户很多,此时你应该在系统高峰期使用上述命令来查看高峰期的对象增长速率。然后,你还需要在非高峰的日常时间段内查看对象的增长速率。

按照上述方法,你可以清晰地了解线上系统在高峰和日常两个时间段内的对象增长速率。

7、Young GC的触发频率和每次耗时

接下来,我们需要了解Young GC的触发频率以及每次Young GC的耗时。

要预测Young GC的触发频率,我们可以基于系统在高峰期和日常运行时的对象增长速率来推测。例如,假设Eden区的内存为800MB,如果在高峰期每秒新增5MB对象,那么大约每3分钟就会触发一次Young GC。而在日觉期,如果每秒新增0.5MB对象,那么大约每半小时会触发一次Young GC。

至于每次Young GC的平均耗时,我们可以通过jstat工具来获取。jstat可以告诉我们系统已经发生了多少次Young GC以及这些Young GC的总耗时。例如,如果系统运行24小时后共发生了260次Young GC,总耗时为20秒,那么平均下来每次Young GC大概耗时几十毫秒。

通过这些信息,我们可以大致了解到每次Young GC会导致系统停顿几十毫秒。

8、每次Young GC后有多少对象是存活和进入老年代

接下来,我们希望能够了解每次Young GC之后,有多少对象能够幸存下来,以及有多少对象会进入老年代。

实际上,我们无法直接观察到每次Young GC后有多少对象会幸存下来,但是我们可以通过一些方法大致估计出来。之前我们已经计算出在高峰期时,多长时间会发生一次Young GC,例如每3分钟就会有一次Young GC。

那么此时,我们可以执行以下jstat命令:jstat -gc PID 180000 10。这就相当于让它每隔三分钟执行一次统计,连续执行10次。

此时,大家可观察一下,每隔三分钟后发生了一次Young GC,然后Eden、Survivor和老年代的对象变化情况。

正常情况下,Eden区会在几乎填满后重新变得对象很少,比如800MB的空间只使用了几十MB。Survivor区肯定会放入一些幸存对象,老年代可能会增加一些对象的占用。所以,这里的关键是观察老年代的对象增长速率。

从正常的角度来看,老年代的对象不太可能持续快速增长,因为普通系统实际上没有那么多长期存活的对象。如果你发现每次Young GC后,老年代对象都会增长几十MB,那很可能是你每次Young GC后幸存对象太多了。

幸存对象过多可能导致在放入Survivor区域后触发动态年龄判定规则进入老年代,也可能是Survivor区域放不下了,所以大部分幸存对象进入了老年代。这是最常见的情况。如果你的老年代每次在Young GC后只新增几百KB或几MB的对象,这种情况还算可以接受,但如果老年代对象快速增长,那一定是不正常的。

因此,通过上述观察策略,你可以了解到每次Young GC后有多少对象是幸存的,实际上,Survivor区域里的对象和进入老年代的对象都是幸存的。

你也可以了解老年代对象的增长速率,例如每隔3分钟一次Young GC,每次会有50MB对象进入老年代,这就是老年代对象的增长速率,即每隔3分钟增长50MB。

9、Full GC的触发时机和耗时

只要我们掌握了老年代对象的增长速率,那么Full GC的触发时机就能清晰明了。举个例子,假设老年代总共有800MB的内存,如果每3分钟新增50MB的对象,那么大约每小时就会触发一次Full GC。

通过jstat工具,我们可以观察到系统运行过程中的Full GC次数以及总耗时。例如,如果系统共执行了10次Full GC,总耗时为30秒,那么平均每次Full GC大约需要耗费3秒左右的时间。

10、本文总结

通过本文对jstat命令的详细介绍,结合我们之前学习过的JVM运行原理,我们向大家传授了一套分析线上系统JVM运行情况的技巧。

大家可以轻松掌握并灵活运用jstat这个实用工具,轻松地掌控线上JVM运行的详细情况。在了解JVM的具体运行情况后,我们可以有针对性地进行优化,提高系统性能。