当前位置:   article > 正文

Java命令行工具之 jstat_jstat -gc命令详解

jstat -gc命令详解

 

 

jstat 是JDK中提供的一个命令行工具,主要用来打印JVM 性能数据相关的统计数据。主要包含以下几个方面:

  • 垃圾回收(GC)方面数据

  • 编译(Compilation)相关数据

  • 类加载信息(Class Loader)

jstat 最大的优势就是可以在JVM运行时,实时的抓取这些数据。

 

如何启动 jstat      

jstat 使用很简单,只要在命令行中执行如下命令:

 jstat -gc -t 11256 10000 10

参数解释:

  • -gc :打印相关的统计参数

  • -t:  在每行日志之前加上JVM的启动时间

  • 11256 : 目标Java进程的ID

  • 10000: jstat命令执行间隔时间(milliseconds),10000表示每10s打印一行日志

  • 10: jstat命令的执行次数,(和上面的时间间隔一起,表示jstat会每10s执行1次,总共执行10次).

执行结果:

上面命令行执行后的结果类似于以下格式:

 

jstat 打印的信息很多且杂,要了解其中各部分代表的意义需要先对JVM中堆内存有一定的了解。在JVM中,堆内存分为年轻代老年代;年轻代因为使用复制回收算法,也被分为Eden区S0区 和 S1区,如下图所示:

 

了解了堆内存的分布,再看jstat的打印参数就很容易理解了。

参数意义:

  • time :  JVM启动时间(单位为秒)

  • S0C :年轻代中S0区的容量 (字节)

  • S1C :年轻代中S1区的容量 (字节)

  • S0U :年轻代中S0区目前已使用空间 (字节)

  • S1U :年轻代中S1区目前已使用空间 (字节)

  • EC :年轻代中Eden区的容量 (字节)

  • EU :年轻代中Eden区目前已使用空间 (字节)

  • OC :老年代的容量 (字节)

  • OU :老年代目前已使用空间 (字节)

  • YGC :从应用程序启动到采样时年轻代中GC次数

  • YGCT :从应用程序启动到采样时年轻代中GC所用时间(s)

  • FGC :从应用程序启动到采样时老年代(全GC)GC次数

  • FGCT :从应用程序启动到采样时老年代(全GC)GC所用时间(s)

  • GCT:从应用程序启动到采样时GC用的总时间(s)

解析执行结果

通过上面的jstat打印日志,基本可以得出如下结论:

 

注意:其实除了 -gc 之外,jstat 还有很多其它子命令

 

实战演练

我一般使用 jstat 做两个用途

  1.  通过查看 jstat 打印的数据,调整JVM配置参数

  2. 查看代码中是否存在内存泄漏

  3. 1. 调整JVM配置参数 

    如下代码 JstatDemo.java:

 

  1. public class JstatDemo {
  2. static volatile List pigs = new ArrayList();
  3. static volatile int pigsEaten = 0;
  4. static final int ENOUGH_PIGS = 1000;
  5. public static void main(String[] args) throws InterruptedException {
  6. new PigEater().start();
  7. new PigDigester().start();
  8. }
  9. static class PigEater extends Thread {
  10. @Override
  11. public void run() {
  12. while (true) {
  13. pigs.add(new byte[32 * 1024 * 1024]); //32MB
  14. if (pigsEaten > ENOUGH_PIGS) return;
  15. sleep(100);
  16. }
  17. }
  18. }
  19. static class PigDigester extends Thread {
  20. @Override
  21. public void run() {
  22. long start = System.currentTimeMillis();
  23. while (true) {
  24. sleep(2000);
  25. pigsEaten+=pigs.size();
  26. pigs = new ArrayList();
  27. if (pigsEaten > ENOUGH_PIGS) {
  28. System.out.format("Digested %d pigs in %d ms.%n",pigsEaten, System.currentTimeMillis()-start);
  29. return;
  30. }
  31. }
  32. }
  33. }
  34. static void sleep(int ms) {
  35. try {
  36. Thread.sleep(ms);
  37. } catch (Exception e) {
  38. e.printStackTrace();
  39. }
  40. }
  41. }
  42. 亲爱的小伙伴们,有需要java面试文档资料的请点赞收藏和转发哦,关注我之后点进我的主页右上角私信(555)即刻领取免费资料哦或点击:https://shimo.im/docs/aBAYVxlBX6IDge3j

上述代码分别使用两个线程 PigEater 和 PigEater 来代表线程执行的吞吐量。

接下来通过配置不同JVM配置,分别执行上述代码,并查看代码执行效果。

配置1:

  • 设置堆内存大小为4G (-Xms4g –Xmx4g)

  • 使用CMS回收器回收老年代(-XX:+UseConcMarkSweepGC),使用Parallel回收算法回收年轻代(-XX:+UseParNewGC)

  • 设置年轻代Eden区大小为512M(-Xmn512m) 

配置2:

  • 设置堆内存大小为2G(-Xms2g –Xmx2g)

  • 使用Parallel回收器回收年轻代和老年代垃圾对象 (-XX:+UseParallelGC)

  • 设置年轻代Eden区为1536M(-Xmn1536m)

分别通过如下两个命令执行配置1和配置2代码,在执行过程中同时执行 jstat 命令

 执行结果为:

可以看出配置2的执行时间更短,执行效率更高。可是配置1的分配内存是配置2的2倍,按照正常思维应该更快一些,而结果却是相反。这是为什么呢?如果要分析原因,可以借助于 jstat 工具来分析。

配置1执行 jstat 结果如下:

  1. Timestamp S0C      S1C      S0U      S1U     EC      EU        OC         OU        PC      PU      YGC    YGCT   FGC    FGCT    GCT
  2. 594.0   174720.0 174720.0 163844.1  0.0   174848.0 131074.1 3670016.0  2621693.5  21248.0 2580.9   1006   63.182  116   0.236   63.419
  3. 595.0   174720.0 174720.0 163842.1  0.0   174848.0 65538.0  3670016.0  3047677.9  21248.0 2580.9   1008   63.310  117   0.236   63.546
  4. 596.1   174720.0 174720.0 98308.0 163842.1 174848.0 163844.2 3670016.0   491772.9  21248.0 2580.9   1010   63.354  118   0.240   63.595
  5. 597.0   174720.0 174720.0  0.0   163840.1 174848.0 131074.1 3670016.0   688380.1  21248.0 2580.9   1011   63.482  118   0.240   63.723

配置2执行 jstat 结果如下:

  1. Timestamp S0C      S1C     S0U    S1U     EC        EU        OC         OU        PC     PU     YGC     YGCT  FGC   FGCT     GCT
  2. 539.3   164352.0 164352.0  0.0    0.0   1211904.0 98306.0   524288.0   164352.2  21504.0 2579.2   27    2.969  141   8.441   11.409
  3. 540.3   164352.0 164352.0  0.0    0.0   1211904.0 425986.2  524288.0   164352.2  21504.0 2579.2   27    2.969  141   8.441   11.409
  4. 541.4   164352.0 164352.0  0.0    0.0   1211904.0 720900.4  524288.0   164352.2  21504.0 2579.2   27    2.969  141   8.441   11.409
  5. 542.3   164352.0 164352.0  0.0    0.0   1211904.0 1015812.6 524288.0   164352.2  21504.0 2579.2   27    2.969  141   8.441   11.409

仔细看可以看出配置1经历了1129次GC事件(YGC + FGC),用时63.723秒(YGCT + FGCT)。而配置2只是经历了168次GC回收,用时11.409秒。大量的GC会造成程序性能降低。

结论:
虽然配置2比配置1的内存小1倍,但是吞吐量却比配置1更高。因此JVM参数的配置还是要根据实际项目、实际情况。

2. 查看代码是否存在内存泄漏

在长时间运行的Java程序中,可以通过运行 jstat 命令连续获取多行GC相关数据,并取这几行数据中的OU(也就是老年代已用容量)的值。

然后,每隔一段较长时间重复一次上述操作,来获取多组OU值。如果这些值呈上升趋势,则说明该Java程序的老年代内存已使用量不断上涨,因此无法被回收的对象在不断增长,很有可能存在内存泄漏。

jstat 缺陷         

很明显 jstat 也不是万能的,最大缺陷就是GC日志不详细。

主要有以下几个方面的信息 jstat 无法获取:

  1. 当多个GC事件发生时,无法获取某单个GC的pause时间

  2. 无法获取sys和user的执行时间

  3. 每次GC事件后,有多少内存被回收掉

因此实际分析问题时,还需要结合其它工具一起分析JVM的性能,其它工具后续会持续介绍。

 

 

 如果本文对你有帮助,别忘了关注,点赞,评论,转发,收藏哟!
     收藏等于白嫖,点赞才是真爱 谢谢0.0

声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/2023面试高手/article/detail/445408
推荐阅读
相关标签
  

闽ICP备14008679号