JVM调优学习笔记(一)

一、GC性能参数

系统的性能可以从三个维度进行衡量:

  • Latency(延迟)
  • Throughput(吞吐量)
  • Capacity(系统容量)

延迟

  • 所有交易必须在10秒内得到响应

以上就是一个延迟指标,对于这类指标,需要确保在交易过程中, GC暂停不能占用太多时间。

bash 复制代码
2015-06-04T13:34:16.974-0200: 2.578: [Full GC (Ergonomics)
        [PSYoungGen: 93677K->70109K(254976K)] 
        [ParOldGen: 499597K->511230K(761856K)] 
        593275K->581339K(1016832K),
        [Metaspace: 2936K->2936K(1056768K)]
    , 0.0713174 secs]
    [Times: user=0.21 sys=0.02, real=0.07 secs

从上方的GC日志中可见,当前事件将应用线程暂停了0.0713174秒(real)。

吞吐量

  • 解决方案每天必须处理 100万个订单

以上是一个吞吐量指标,吞吐量需求是在给定的时间内, 系统必须完成多少个操作。因此,和延迟需求类似, GC调优也需要确定GC行为所消耗的总时间。在上方的日志中可见,0.23s(user + sys = 0.21 + 0.02 s)是这段时间内 GC 暂停占用 cpu 资源的时间。

系统容量

系统容量(Capacity)需求,是在达成吞吐量和延迟指标的情况下,对硬件环境的额外约束。

二、简单的调优实验

在启动程序时,可以通过以下参数打开GC日志:

bash 复制代码
-XX:+PrintGCDetails -XX:+PrintGCDateStamps -XX:+PrintGCTimeStamps -Xloggc:C:\Producer_gc.log

基于日志中的信息, 可以通过三个优化目标来提升性能:

  1. 确保最坏情况下,GC暂停时间不超过预定阈值(延迟)
  2. 确保线程暂停的总时间不超过预定阀值(吞吐量)
  3. 在确保达到延迟和吞吐量指标的情况下, 降低硬件配置以及成本(容量)

堆内存大小GC算法 是程序运行时可选择的部分参数:

堆内存大小(Heap) GC算法(GC Algorithm) 有效时间比(Useful work) 最长停顿时间(Longest pause)
-Xmx12g -XX:+UseConcMarkSweepGC 89.8% 560 ms
-Xmx12g -XX:+UseParallelGC 91.5% 1,104 ms
-Xmx8g -XX:+UseConcMarkSweepGC 66.3% 1,610 ms

三、JVM分区

JVM监控工具是定位问题的有效方法,在学习监控工具前,需要理解"新生代"和"老生代"的含义。常说的 新生代(Young Generation)老年代(Old Generation) ,本质上是 按对象生命周期特征划分的两块堆内存区域

  • 新生代 :主要存放刚创建不久、生命周期短的对象
  • 老年代 :主要存放经历过多次垃圾回收后仍然存活、生命周期较长的对象

JVM 采用了经典的 分代收集思想(Generational Collection)

  • 大量短命对象 ,用频繁但成本较低的方式回收(Young GC
  • 少量长寿对象 ,用相对更重但不那么频繁的方式回收(Full GC

JVM 运行 Java 程序时,内存是按对象生命周期和用途进行分区管理:

  • Eden:新对象最先分配的地方
  • Survivor:在 Young GC 后仍然存活的对象暂存区
  • Old(Old Generation) :存放长期存活对象
  • Metaspace:存放类元数据,不在 Java 堆里,使用本地内存
css 复制代码
Java Heap
├── Young Generation
│   ├── Eden
│   ├── Survivor From
│   └── Survivor To
└── Old Generation

Non-Heap
└── Metaspace

通过观察不同分区的使用情况,可以帮助我们定位问题。

四、JVM监控工具

以下是常用的JVM监控工具:

1、jps

jps 是 JDK 自带的一个 Java 进程查看工具,常用参数包括:

bash 复制代码
jps    # 只输出PID和主类名
jps -l # 输出完整类名 / Jar 路径
jps -m # 输出传给 main 方法的参数
jps -v # 输出 JVM 参数
jps -q # 只输出 PID,适用于脚本处理

jps 命令只会输出 Java 进程信息,输出内容精简,便于查看。

2、jstat

bash 复制代码
jstat [option] <vmid> [interval] [count]
  • interval:执行间隔时间
  • count:执行次数

常用 jstat 命令包括:

-gc

-gc 命令可以查看堆各区域容量与使用量,jstat -gc 12345命令输出的结果如下:

bash 复制代码
 S0C    S1C    S0U    S1U      EC       EU        OC         OU       MC     MU    CCSC   CCSU   YGC     YGCT    FGC    FGCT     GCT
16384.0 17920.0  0.0    0.0   262144.0 132572.2  197120.0   24724.2   59160.0 55833.9 8232.0 7676.9      8    0.051   3      0.155    0.206
  • S0C / S1C:Survivor0 / Survivor1 容量
  • S0U / S1U:Survivor0 / Survivor1 已使用大小
  • EC / EU:Eden 容量 / 已使用大小
  • OC / OU:Old 区容量 / 已使用大小
  • MC / MU:Metaspace 容量 / 已使用大小
  • CCSC / CCSU:Compressed Class Space 容量 / 已使用大小
  • YGC / YGCT:Young GC 次数 / Young GC 总耗时
  • FGC / FGCT:Full GC 次数 / Full GC 总耗时
  • GCT:GC 总耗时

-gcutil

快速查看各区域使用率百分比,相比-gc,该方法输出内容更加简洁直观:

bash 复制代码
  S0     S1     E      O      M     CCS    YGC     YGCT    FGC    FGCT     GCT
  0.00   0.00  50.57  12.54  94.38  93.26      8    0.051     3    0.155    0.206
  • S0 / S1:Survivor 区使用率
  • E:Eden 区使用率
  • O:Old 区使用率
  • M:Metaspace 使用率
  • CCS:Compressed Class Space 使用率
  • YGC / YGCT:Young GC 次数 / 耗时
  • FGC / FGCT:Full GC 次数 / 耗时
  • GCT:GC 总耗时

-gccapacity

查看 JVM 各区域当前容量、最小值、最大值

bash 复制代码
 NGCMN    NGCMX     NGC     S0C   S1C       EC      OGCMN      OGCMX       OGC         OC       MCMN     MCMX      MC     CCSMN    CCSMX     CCSC    YGC    FGC
 86016.0 1374720.0 280064.0 14848.0 17408.0 245248.0   172032.0  2749952.0   204800.0   204800.0      0.0 1099776.0  59160.0      0.0 1048576.0   8232.0      8     3
  • NGCMN / NGCMX:年轻代最小容量 / 年轻代最大容量
  • NGC:当前年轻代总容量
  • S0C / S1C:Survivor 0 容量 / Survivor 1 容量
  • EC:Eden 区当前容量
  • OGCMN / OGCMX:老年代最小容量 / 老年代最大容量
  • OGC / OC:当前老年代容量
  • MCMN / MCMX:Metaspace 最小容量 / Metaspace 最大容量
  • MC:当前 Metaspace 容量
  • CCSMN / CCSMX:Compressed Class Space 最小容量 / 最大容量
  • CCSC:当前 Compressed Class Space 容量
  • YGC:Young GC 次数
  • FGC:Full GC 次数

-gcnewcapcity-gcoldcapacity可以专注于查看新生代、老年代的使用情况

-class

查看类加载情况

bash 复制代码
Loaded  Bytes  Unloaded  Bytes     Time
 13233 23485.6        0     0.0       6.95
  • Time:已加载类耗时

五、简单的GC监控实验

jstat是监控GC的最常用工具,对于 jstat 常用的参数,可以通过jstat -options查看。

执行 jstat -gc -t 2428 1s 可以让 jstat 每秒向标准输出输出一行新内容:

bash 复制代码
Timestamp  S0C    S1C    S0U    S1U      EC       EU        OC         OU       MC     MU    CCSC   CCSU   YGC     YGCT    FGC    FGCT     GCT   
200.0    8448.0 8448.0 8448.0  0.0   67712.0  67712.0   169344.0   169344.0  21248.0 20534.3 3072.0 2807.7     34    0.720  658   133.684  134.404
201.0    8448.0 8448.0 8448.0  0.0   67712.0  67712.0   169344.0   169343.2  21248.0 20534.3 3072.0 2807.7     34    0.720  662   134.712  135.432
202.0    8448.0 8448.0 8102.5  0.0   67712.0  67598.5   169344.0   169343.6  21248.0 20534.3 3072.0 2807.7     34    0.720  667   135.840  136.559
203.0    8448.0 8448.0 8126.3  0.0   67712.0  67702.2   169344.0   169343.6  21248.0 20547.2 3072.0 2807.7     34    0.720  669   136.178  136.898
204.0    8448.0 8448.0 8126.3  0.0   67712.0  67702.2   169344.0   169343.6  21248.0 20547.2 3072.0 2807.7     34    0.720  669   136.178  136.898
205.0    8448.0 8448.0 8134.6  0.0   67712.0  67712.0   169344.0   169343.5  21248.0 20547.2 3072.0 2807.7     34    0.720  671   136.234  136.954
206.0    8448.0 8448.0 8134.6  0.0   67712.0  67712.0   169344.0   169343.5  21248.0 20547.2 3072.0 2807.7     34    0.720  671   136.234  136.954
207.0    8448.0 8448.0 8154.8  0.0   67712.0  67712.0   169344.0   169343.5  21248.0 20547.2 3072.0 2807.7     34    0.720  673   136.289  137.009
208.0    8448.0 8448.0 8154.8  0.0   67712.0  67712.0   169344.0   169343.5  21248.0 20547.2 3072.0 2807.7     34    0.720  673   136.289  137.009

jstat 的输出可以体现很多问题:

  • 最后一列 GCT , 与JVM的总运行时间 Timestamp 的比值, 就是GC 的开销。如果每一秒内, GCT 的值都会明显增大, 与总运行时间相比, 就暴露出GC开销过大的问题. 一般来讲, 超过10%的GC开销都是有问题的。
  • YGCFGC 列的快速变化往往也是有问题的征兆。频繁的GC暂停会累积,并导致更多的线程停顿(stop-the-world pauses), 进而影响吞吐量。
  • 如果看到 OU 列中,老年代的使用量约等于老年代的最大容量(OC), 并且不降低的话, 就表示虽然执行了老年代GC, 但基本上属于无效GC。
相关推荐
NCIN EXPE2 小时前
SpringBoot教程(三十二) SpringBoot集成Skywalking链路跟踪
spring boot·后端·skywalking
lagrahhn2 小时前
python面向对象中__new__和__init__区别
后端·python·程序员
echome8882 小时前
Go 语言并发编程:Goroutine 与 Channel 实战指南
后端·golang
清汤饺子2 小时前
OpenSpec:让 AI 编程从"开盲盒"到"先签字再干活"
前端·javascript·后端
RATi GORI2 小时前
Spring Boot 整合 Keycloak
java·spring boot·后端
她说..2 小时前
Spring单例Bean线程安全问题 深度解析
java·后端·安全·spring·springboot
DROm RAPS3 小时前
springboot整合libreoffice(两种方式,使用本地和远程的libreoffice);docker中同时部署应用和libreoffice
spring boot·后端·docker
爱丽_3 小时前
Spring Boot 启动链路:自动装配、条件注解与排错方法论
java·spring boot·后端
RDCJM3 小时前
Spring Boot项目接收前端参数的11种方式
前端·spring boot·后端