JVM内存管理, GC调优

官方资料

JDK8 https://docs.oracle.com/javase/8/docs/technotes/guides/vm/gctuning/
JDK11 https://docs.oracle.com/en/java/javase/11/gctuning/index.html
JVM内存管理 https://www.oracle.com/technetwork/java/javase/tech/memorymanagement-whitepaper-1-150020.pdf

年轻代与年老代

JVM Heap分为年轻代, 年老代

区域名称 所属 作用
eden 年轻代 取名来自伊甸园, 大多数java对象都是从eden区分配内存
survivor 年轻代 s1, s2两个区组成, 其中一个保持是空的,
当另外一个区满了之后, 会将对象转移到空的区域中, 如果对象如此倒腾多次之后, 仍然存活, 则将这个对象转移到年老代
old 年老代 年轻代中存活下来的对象最终会转移到年老代中, 有些较大的对象, 也可能直接分配在年老代

垃圾回收gc

minor gc

年轻代内存满了之后, 会执行minor gc

full gc

也叫major gc
年老代内存满了之后, 会执行full gc

gc时, 会导致jvm暂停运行. 因此gc调优的重要指标:

  1. jvm停顿时间. 停顿时间太长, 肯定不行

Concurrent Mark-Sweep (CMS) Collector

https://docs.oracle.com/en/java/javase/11/gctuning/concurrent-mark-sweep-cms-collector.html#GUID-FF8150AC-73D9-4780-91DD-148E63FA1BFF
服务器端通常使用这个回收器. 因为服务端通常会分配很大我内存, 比如2个G. 靠串行之类的回收, 肯定不行.
流程:

  1. initial mark, 暂停jvm, 生成一个存活对象的集合
  2. 恢复jvm, 追踪对象
  3. 再暂停jvm, 再次追踪
  4. 恢复运行, 清扫垃圾对象.

缺点:
会产生内存碎片.

其它的回收器没用过, 不说了, 看文档吧

GC调优

实现目标:

  1. 单次GC导致JVM暂停的时间最短
  2. GC总时间与程序总运行时间的占比最小

这个就靠经验了.

jstat 观察GC运行状态

https://docs.oracle.com/javase/8/docs/technotes/tools/unix/jstat.html
比如, 观察年轻代的GC情况

jstat -gcnew 14344 2000
 S0C    S1C    S0U    S1U   TT MTT  DSS      EC       EU          YGC     YGCT  
102400.0 102400.0 54963.8    0.0 15  15 51200.0 204800.0 199720.3 175474 7723.720
102400.0 102400.0    0.0 72970.3  1  15 51200.0 204800.0  28428.1 175475 7723.760

YGC为young gc的次数
YGCT为young gc所花费的总时间,
本例中, 本次gc花费了40毫秒
同样, 可以打印old gc的状况

jstat -gcold 14344 2000

通过GC日志

https://docs.oracle.com/javase/8/docs/technotes/guides/vm/gctuning/cms.html
https://docs.oracle.com/en/java/javase/11/gctuning/concurrent-mark-sweep-cms-collector.html#GUID-704EEEB0-EE76-44D4-BA18-FA92F5A7574A
下例中 (需要启用GC日志)
major gc时, JVM暂停的时间包括:
Initial Mark 0.0051491 secs
CMS-remark 0.2138783 secs

minor gc的时间为0.0463569 (Allocation Failure开始那行)

2019-02-12T14:42:57.342+0800: 6099136.165: [GC (CMS Initial Mark) [1 CMS-initial-mark: 502696K(546176K)] 515234K(750976K), 0.0051491 secs] [Times: user=0.01 sys=0.00, real=0.00 secs] 
2019-02-12T14:42:57.348+0800: 6099136.170: [CMS-concurrent-mark-start]
2019-02-12T14:42:57.630+0800: 6099136.453: [CMS-concurrent-mark: 0.283/0.283 secs] [Times: user=0.19 sys=0.11, real=0.28 secs] 
2019-02-12T14:42:57.630+0800: 6099136.453: [CMS-concurrent-preclean-start]
2019-02-12T14:42:57.651+0800: 6099136.473: [CMS-concurrent-preclean: 0.020/0.020 secs] [Times: user=0.01 sys=0.01, real=0.02 secs] 
2019-02-12T14:42:57.651+0800: 6099136.473: [CMS-concurrent-abortable-preclean-start]
 CMS: abort preclean due to time 
2019-02-12T14:43:02.760+0800: 6099141.582: [CMS-concurrent-abortable-preclean: 1.999/5.109 secs] [Times: user=7.12 sys=0.00, real=5.11 secs] 
2019-02-12T14:43:02.760+0800: 6099141.583: [GC (CMS Final Remark) [YG occupancy: 53344 K (204800 K)]
2019-02-12T14:43:02.760+0800: 6099141.583: [Rescan (parallel) , 0.0137956 secs]
2019-02-12T14:43:02.774+0800: 6099141.597: [weak refs processing, 0.1713338 secs]
2019-02-12T14:43:02.946+0800: 6099141.768: [class unloading, 0.0086490 secs]
2019-02-12T14:43:02.954+0800: 6099141.777: [scrub symbol table, 0.0026173 secs]
2019-02-12T14:43:02.957+0800: 6099141.779: [scrub string table, 0.0006322 secs][1 CMS-remark: 502696K(546176K)] 556040K(750976K), 0.2138783 secs] [Times: user=0.00 sys=0.00, real=0.22 secs] 
2019-02-12T14:43:02.977+0800: 6099141.799: [CMS-concurrent-sweep-start]
2019-02-12T14:43:03.193+0800: 6099142.016: [CMS-concurrent-sweep: 0.216/0.216 secs] [Times: user=0.00 sys=0.00, real=0.21 secs] 
2019-02-12T14:43:03.193+0800: 6099142.016: [CMS-concurrent-reset-start]
2019-02-12T14:43:03.195+0800: 6099142.017: [CMS-concurrent-reset: 0.001/0.001 secs] [Times: user=0.00 sys=0.00, real=0.01 secs] 

2019-02-12T14:43:03.805+0800: 6099142.628: [GC (Allocation Failure) 
2019-02-12T14:43:03.806+0800: 6099142.628: [ParNew: 146395K->7398K(204800K), 0.0462122 secs] 439184K->300273K(750976K), 0.0463569 secs] [Times: user=0.00 sys=0.00, real=0.04 secs] 

设置Heap大小

  1. Heap上限与初始值
    -Xms1g -Xmx1g, 设置成相等. (对于服务端来说, 就是要一直稳定运行)

  2. 年轻代大小
    -XX:NewRatio=2, 年轻代与年老代的比值为1:2
    -XX:NewSize=100m, 年轻代大小设置成指定的值
    -XX:SurvivorRatio=2, eden区与单个Survivor区的比率大小

设置CMS及GC相关参数

-XX:MaxGCPauseMillis=250 设置GC暂停允许的最大时间
-XX:ParallelGCThreads=4 并行GC的线程数
-XX:+UseConcMarkSweepGC 指定使用CMS回收器

开启GC日志

-XX:+UseGCLogFileRotation -XX:NumberOfGCLogFiles=3 -XX:+PrintGCDetails -XX:+PrintGCDateStamps \
-XX:PrintFLSStatistics=2 -XX:+PrintGCApplicationStoppedTime -XX:+PrintHeapAtGC \
-XX:GCLogFileSize=2048K  -XX:+PrintFlagsFinal

猜你喜欢

转载自blog.csdn.net/wzj_whut/article/details/87086452