@@ -13,19 +13,110 @@ tags:
13
13
14
14
<!-- TOC depthFrom:2 depthTo:3 -->
15
15
16
+ - [ JVM 调优概述] ( #jvm-调优概述 )
17
+ - [ 性能定义] ( #性能定义 )
18
+ - [ 调优原则] ( #调优原则 )
19
+ - [ GC 优化的过程] ( #gc-优化的过程 )
16
20
- [ 命令] ( #命令 )
17
21
- [ jmap] ( #jmap )
18
22
- [ jstack] ( #jstack )
19
23
- [ jps] ( #jps )
20
24
- [ jstat] ( #jstat )
21
25
- [ jhat] ( #jhat )
22
26
- [ jinfo] ( #jinfo )
23
- - [ GC 优化配置] ( #gc-优化配置 )
24
- - [ GC 类型设置] ( #gc-类型设置 )
27
+ - [ HotSpot VM 参数] ( #hotspot-vm-参数 )
28
+ - [ JVM 内存配置] ( #jvm-内存配置 )
29
+ - [ GC 类型配置] ( #gc-类型配置 )
30
+ - [ 辅助配置] ( #辅助配置 )
31
+ - [ 典型配置] ( #典型配置 )
32
+ - [ 堆大小设置] ( #堆大小设置 )
33
+ - [ 回收器选择] ( #回收器选择 )
34
+ - [ JVM 实战] ( #jvm-实战 )
35
+ - [ 分析 GC 日志] ( #分析-gc-日志 )
36
+ - [ 1. 获取 GC 日志] ( #1-获取-gc-日志 )
37
+ - [ 2. 如何分析 GC 日志] ( #2-如何分析-gc-日志 )
25
38
- [ 资料] ( #资料 )
26
39
27
40
<!-- /TOC -->
28
41
42
+ ## JVM 调优概述
43
+
44
+ ### 性能定义
45
+
46
+ - 吞吐量 - 指不考虑 GC 引起的停顿时间或内存消耗,垃圾收集器能支撑应用达到的最高性能指标。
47
+ - 延迟 - 其度量标准是缩短由于垃圾啊收集引起的停顿时间或者完全消除因垃圾收集所引起的停顿,避免应用运行时发生抖动。
48
+ - 内存占用 - 垃圾收集器流畅运行所需要的内存数量。
49
+
50
+ ### 调优原则
51
+
52
+ GC 优化的两个目标:
53
+
54
+ 1 . 将进入老年代的对象数量降到最低
55
+ 2 . 减少 Full GC 的执行时间
56
+
57
+ GC 优化的基本原则是:将不同的 GC 参数应用到两个及以上的服务器上然后比较它们的性能,然后将那些被证明可以提高性能或减少 GC 执行时间的参数应用于最终的工作服务器上。
58
+
59
+ #### 将进入老年代的对象数量降到最低
60
+
61
+ 除了可以在 JDK7 及更高版本中使用的 G1 收集器以外,其他分代 GC 都是由 Oracle JVM 提供的。关于分代 GC,就是对象在 Eden 区被创建,随后被转移到 Survivor 区,在此之后剩余的对象会被转入老年代。也有一些对象由于占用内存过大,在 Eden 区被创建后会直接被传入老年代。老年代 GC 相对来说会比新生代 GC 更耗时,因此,减少进入老年代的对象数量可以显著降低 Full GC 的频率。你可能会以为减少进入老年代的对象数量意味着把它们留在新生代,事实正好相反,新生代内存的大小是可以调节的。
62
+
63
+ #### 降低 Full GC 的时间
64
+
65
+ Full GC 的执行时间比 Minor GC 要长很多,因此,如果在 Full GC 上花费过多的时间(超过 1s),将可能出现超时错误。
66
+
67
+ - 如果** 通过减小老年代内存来减少 Full GC 时间** ,可能会引起 OutOfMemoryError 或者导致 Full GC 的频率升高。
68
+ - 另外,如果** 通过增加老年代内存来降低 Full GC 的频率** ,Full GC 的时间可能因此增加。
69
+
70
+ 因此,你** 需要把老年代的大小设置成一个“合适”的值** 。
71
+
72
+ ** GC 优化需要考虑的 JVM 参数**
73
+
74
+ | ** 类型** | ** 参数** | ** 描述** |
75
+ | -------------- | ------------------- | ----------------------------- |
76
+ | 堆内存大小 | ` -Xms ` | 启动 JVM 时堆内存的大小 |
77
+ | | ` -Xmx ` | 堆内存最大限制 |
78
+ | 新生代空间大小 | ` -XX:NewRatio ` | 新生代和老年代的内存比 |
79
+ | | ` -XX:NewSize ` | 新生代内存大小 |
80
+ | | ` -XX:SurvivorRatio ` | Eden 区和 Survivor 区的内存比 |
81
+
82
+ GC 优化时最常用的参数是` -Xms ` ,` -Xmx ` 和` -XX:NewRatio ` 。` -Xms ` 和` -Xmx ` 参数通常是必须的,所以` NewRatio ` 的值将对 GC 性能产生重要的影响。
83
+
84
+ 有些人可能会问** 如何设置永久代内存大小** ,你可以用` -XX:PermSize ` 和` -XX:MaxPermSize ` 参数来进行设置,但是要记住,只有当出现` OutOfMemoryError ` 错误时你才需要去设置永久代内存。
85
+
86
+ ### GC 优化的过程
87
+
88
+ GC 优化的过程和大多数常见的提升性能的过程相似,下面是笔者使用的流程:
89
+
90
+ #### 1.监控 GC 状态
91
+
92
+ 你需要监控 GC 从而检查系统中运行的 GC 的各种状态。
93
+
94
+ #### 2.分析监控结果后决定是否需要优化 GC
95
+
96
+ 在检查 GC 状态后,你需要分析监控结构并决定是否需要进行 GC 优化。如果分析结果显示运行 GC 的时间只有 0.1-0.3 秒,那么就不需要把时间浪费在 GC 优化上,但如果运行 GC 的时间达到 1-3 秒,甚至大于 10 秒,那么 GC 优化将是很有必要的。
97
+
98
+ 但是,如果你已经分配了大约 10GB 内存给 Java,并且这些内存无法省下,那么就无法进行 GC 优化了。在进行 GC 优化之前,你需要考虑为什么你需要分配这么大的内存空间,如果你分配了 1GB 或 2GB 大小的内存并且出现了` OutOfMemoryError ` ,那你就应该执行** 堆快照(heap dump)** 来消除导致异常的原因。
99
+
100
+ > 注意:
101
+
102
+ > ** 堆快照(heap dump)** 是一个用来检查 Java 内存中的对象和数据的内存文件。该文件可以通过执行 JDK 中的` jmap ` 命令来创建。在创建文件的过程中,所有 Java 程序都将暂停,因此,不要在系统执行过程中创建该文件。
103
+
104
+ > 你可以在互联网上搜索 heap dump 的详细说明。
105
+
106
+ #### 3.设置 GC 类型/内存大小
107
+
108
+ 如果你决定要进行 GC 优化,那么你需要选择一个 GC 类型并且为它设置内存大小。此时如果你有多个服务器,请如上文提到的那样,在每台机器上设置不同的 GC 参数并分析它们的区别。
109
+
110
+ #### 4.分析结果
111
+
112
+ 在设置完 GC 参数后就可以开始收集数据,请在收集至少 24 小时后再进行结果分析。如果你足够幸运,你可能会找到系统的最佳 GC 参数。如若不然,你还需要分析输出日志并检查分配的内存,然后需要通过不断调整 GC 类型/内存大小来找到系统的最佳参数。
113
+
114
+ #### 5.如果结果令人满意,将参数应用到所有服务器上并结束 GC 优化
115
+
116
+ 如果 GC 优化的结果令人满意,就可以将参数应用到所有服务器上,并停止 GC 优化。
117
+
118
+ 在下面的章节中,你将会看到上述每一步所做的具体工作。
119
+
29
120
## 命令
30
121
31
122
### jmap
@@ -158,34 +249,142 @@ option 参数:
158
249
> - -flags : 不需要 args 参数,输出所有 JVM 参数的值
159
250
> - -sysprops : 输出系统属性,等同于 System.getProperties()
160
251
161
- ## GC 优化配置
252
+ ## HotSpot VM 参数
253
+
254
+ > 详细参数说明请参考官方文档:[ Java HotSpot VM Options] ( http://www.oracle.com/technetwork/java/javase/tech/vmoptions-jsp-140102.html ) ,这里仅列举常用参数。
162
255
163
- | 配置 | 描述 |
164
- | --------------- | ---------------- |
165
- | -Xms | 初始化堆内存大小 |
166
- | -Xmx | 堆内存最大值 |
167
- | -Xmn | 新生代大小 |
168
- | -XX: PermSize | 初始化永久代大小 |
169
- | -XX: MaxPermSize | 永久代最大容量 |
256
+ ### JVM 内存配置
170
257
171
- ## GC 类型设置
258
+ | 配置 | 描述 |
259
+ | ----------------- | -------------------- |
260
+ | ` -Xms ` | 堆空间初始值。 |
261
+ | ` -Xmx ` | 堆空间最大值。 |
262
+ | ` -XX:NewSize ` | 新生代空间初始值。 |
263
+ | ` -XX:MaxNewSize ` | 新生代空间最大值。 |
264
+ | ` -Xmn ` | 新生代空间大小。 |
265
+ | ` -XX:PermSize ` | 永久代空间的初始值。 |
266
+ | ` -XX:MaxPermSize ` | 永久代空间的最大值。 |
267
+
268
+ ### GC 类型配置
172
269
173
270
| 配置 | 描述 |
174
271
| ----------------------- | ----------------------------------------- |
175
272
| -XX:+UseSerialGC | 串行垃圾回收器 |
176
273
| -XX:+UseParallelGC | 并行垃圾回收器 |
274
+ | -XX:+UseParNewGC | 使用 ParNew + Serial Old 垃圾回收器组合 |
177
275
| -XX:+UseConcMarkSweepGC | 并发标记扫描垃圾回收器 |
178
276
| -XX: ParallelCMSThreads = | 并发标记扫描垃圾回收器 = 为使用的线程数量 |
179
277
| -XX:+UseG1GC | G1 垃圾回收器 |
180
278
181
- ``` java
182
- java - Xmx12m - Xms3m - Xmn1m - XX : PermSize = 20m - XX : MaxPermSize = 20m - XX : + UseSerialGC - jar java- application. jar
279
+ ### 辅助配置
280
+
281
+ | 配置 | 描述 |
282
+ | --------------------------------- | ------------------------ |
283
+ | ` -XX:+PrintGCDetails ` | 打印 GC 日志 |
284
+ | ` -Xloggc:<filename> ` | 指定 GC 日志文件名 |
285
+ | ` -XX:+HeapDumpOnOutOfMemoryError ` | 内存溢出时输出堆快照文件 |
286
+
287
+ ## 典型配置
288
+
289
+ ### 堆大小设置
290
+
291
+ ** 年轻代的设置很关键。**
292
+
293
+ JVM 中最大堆大小有三方面限制:
294
+
295
+ 1 . 相关操作系统的数据模型(32-bt 还是 64-bit)限制;
296
+ 2 . 系统的可用虚拟内存限制;
297
+ 3 . 系统的可用物理内存限制。
298
+
299
+ ```
300
+ 整个堆大小 = 年轻代大小 + 年老代大小 + 持久代大小
301
+ ```
302
+
303
+ - 持久代一般固定大小为 64m。使用 ` -XX:PermSize ` 设置。
304
+ - 官方推荐年轻代占整个堆的 3/8。使用 ` -Xmn ` 设置。
305
+
306
+ ### 回收器选择
307
+
308
+ JVM 给了三种选择:串行收集器、并行收集器、并发收集器。
309
+
310
+ ## JVM 实战
311
+
312
+ ### 分析 GC 日志
313
+
314
+ ### 1. 获取 GC 日志
315
+
316
+ 获取 GC 日志有两种方式:
317
+
318
+ - 使用命令动态查看
319
+ - 在容器中设置相关参数打印 GC 日志
320
+
321
+ ` jstat -gc ` 统计垃圾回收堆的行为:
322
+
323
+ ```
324
+ jstat -gc 1262
325
+ S0C S1C S0U S1U EC EU OC OU PC PU YGC YGCT FGC FGCT GCT
326
+ 26112.0 24064.0 6562.5 0.0 564224.0 76274.5 434176.0 388518.3 524288.0 42724.7 320 6.417 1 0.398 6.815
327
+ ```
328
+
329
+ 也可以设置间隔固定时间来打印:
330
+
331
+ ```
332
+ $ jstat -gc 1262 2000 20
183
333
```
184
334
335
+ 这个命令意思就是每隔 2000ms 输出 1262 的 gc 情况,一共输出 20 次
336
+
337
+ Tomcat 设置示例:
338
+
339
+ ```
340
+ JAVA_OPTS="-server -Xms2000m -Xmx2000m -Xmn800m -XX:PermSize=64m -XX:MaxPermSize=256m -XX:SurvivorRatio=4
341
+ -verbose:gc -Xloggc:$CATALINA_HOME/logs/gc.log
342
+ -Djava.awt.headless=true
343
+ -XX:+PrintGCTimeStamps -XX:+PrintGCDetails
344
+ -Dsun.rmi.dgc.server.gcInterval=600000 -Dsun.rmi.dgc.client.gcInterval=600000
345
+ -XX:+UseConcMarkSweepGC -XX:MaxTenuringThreshold=15"
346
+ ```
347
+
348
+ - ` -Xms2000m -Xmx2000m -Xmn800m -XX:PermSize=64m -XX:MaxPermSize=256m `
349
+ Xms,即为 jvm 启动时得 JVM 初始堆大小,Xmx 为 jvm 的最大堆大小,xmn 为新生代的大小,permsize 为永久代的初始大小,MaxPermSize 为永久代的最大空间。
350
+ - ` -XX:SurvivorRatio=4 `
351
+ SurvivorRatio 为新生代空间中的 Eden 区和救助空间 Survivor 区的大小比值,默认是 8,则两个 Survivor 区与一个 Eden 区的比值为 2:8,一个 Survivor 区占整个年轻代的 1/10。调小这个参数将增大 survivor 区,让对象尽量在 survitor 区呆长一点,减少进入年老代的对象。去掉救助空间的想法是让大部分不能马上回收的数据尽快进入年老代,加快年老代的回收频率,减少年老代暴涨的可能性,这个是通过将-XX: SurvivorRatio 设置成比较大的值(比如 65536)来做到。
352
+ - ` -verbose:gc -Xloggc:$CATALINA_HOME/logs/gc.log `
353
+ 将虚拟机每次垃圾回收的信息写到日志文件中,文件名由 file 指定,文件格式是平文件,内容和-verbose: gc 输出内容相同。
354
+ - ` -Djava.awt.headless=true ` Headless 模式是系统的一种配置模式。在该模式下,系统缺少了显示设备、键盘或鼠标。
355
+ - ` -XX:+PrintGCTimeStamps -XX:+PrintGCDetails `
356
+ 设置 gc 日志的格式
357
+ - ` -Dsun.rmi.dgc.server.gcInterval=600000 -Dsun.rmi.dgc.client.gcInterval=600000 `
358
+ 指定 rmi 调用时 gc 的时间间隔
359
+ - ` -XX:+UseConcMarkSweepGC -XX:MaxTenuringThreshold=15 ` 采用并发 gc 方式,经过 15 次 minor gc 后进入年老代
360
+
361
+ ### 2. 如何分析 GC 日志
362
+
363
+ Young GC 回收日志:
364
+
365
+ ```
366
+ 2016-07-05T10:43:18.093+0800: 25.395: [GC [PSYoungGen: 274931K->10738K(274944K)] 371093K->147186K(450048K), 0.0668480 secs] [Times: user=0.17 sys=0.08, real=0.07 secs]
367
+ ```
368
+
369
+ Full GC 回收日志:
370
+
371
+ ```
372
+ 2016-07-05T10:43:18.160+0800: 25.462: [Full GC [PSYoungGen: 10738K->0K(274944K)] [ParOldGen: 136447K->140379K(302592K)] 147186K->140379K(577536K) [PSPermGen: 85411K->85376K(171008K)], 0.6763541 secs] [Times: user=1.75 sys=0.02, real=0.68 secs]
373
+ ```
374
+
375
+ 通过上面日志分析得出,PSYoungGen、ParOldGen、PSPermGen 属于 Parallel 收集器。其中 PSYoungGen 表示 gc 回收前后年轻代的内存变化;ParOldGen 表示 gc 回收前后老年代的内存变化;PSPermGen 表示 gc 回收前后永久区的内存变化。young gc 主要是针对年轻代进行内存回收比较频繁,耗时短;full gc 会对整个堆内存进行回城,耗时长,因此一般尽量减少 full gc 的次数
376
+
377
+ 通过两张图非常明显看出 gc 日志构成:
378
+
379
+ Young GC 日志:![ img] ( http://ityouknow.com/assets/images/2017/jvm/Young%20GC.png )
380
+
381
+ Full GC 日志:![ img] ( http://ityouknow.com/assets/images/2017/jvm/Full%20GC.png )
382
+
185
383
## 资料
186
384
187
- [ JVM(4):Jvm 调优-命令篇] ( http://www.importnew.com/23761.html )
188
- http://www.hollischuang.com/archives/110
189
- https://segmentfault.com/a/1190000005174819
190
- http://www.importnew.com/19264.html
191
- https://blog.csdn.net/lxhandlbb/article/details/76695607
385
+ - [ JVM(4):Jvm 调优-命令篇] ( http://www.importnew.com/23761.html )
386
+ - [ Java 系列笔记(4) - JVM 监控与调优] ( https://www.cnblogs.com/zhguang/p/Java-JVM-GC.html )
387
+ - [ Java 服务 GC 参数调优案例] ( https://segmentfault.com/a/1190000005174819 )
388
+ - [ JVM 调优总结(5):典型配置] ( http://www.importnew.com/19264.html )
389
+ - [ 如何合理的规划一次 jvm 性能调优] ( https://juejin.im/post/59f02f406fb9a0451869f01c )
390
+ - [ jvm 系列(九):如何优化 Java GC「译」] ( http://www.ityouknow.com/jvm/2017/09/21/How-to-optimize-Java-GC.html )
0 commit comments