线上服务的JVM的GC日志如何阅读

1,服务jvm启动参数
2,新生代gc日志详细说明
3,老年代gc日志详细说明
4,从日志总结该服务gc状况
5,参考资料

一、JVM 启动参数

jvm_common_options = -Xms6g -Xmx6g -Xss256K -XX:GCTimeRatio=19 -XX:SurvivorRatio=8 -XX:+AggressiveOpts -XX:ParallelGCThreads=24 -XX:+UseConcMarkSweepGC -XX:+UseCMSCompactAtFullCollection -XX:CMSFullGCsBeforeCompaction=3 -XX:CMSInitiatingOccupancyFraction=70 -XX:+UseFastAccessorMethods -XX:+PrintGCDetails -XX:+PrintGCTimeStamps -XX:+PrintGCDateStamps -XX:+HeapDumpOnOutOfMemoryError -DLog4jContextSelector=org.apache.logging.log4j.core.async.AsyncLoggerContextSelector -Dfile.encoding=UTF-8

[XXXXX@bXXXXXX logs]$ more gc.log 
Java HotSpot(TM) 64-Bit Server VM (25.192-b12) for linux-amd64 JRE (1.8.0_192-b12), built on Oct  6 2018 06:46:09 by "java_re" with gcc 7.3.0
Memory: 4k page, physical 65855392k(7249204k free), swap 8388604k(7810652k free)
CommandLine flags: -XX:+AggressiveOpts -XX:CMSFullGCsBeforeCompaction=3 -XX:CMSInitiatingOccupancyFraction=70 
-XX:GCTimeRatio=19 
-XX:+HeapDumpOnOutOfMemoryError 
-XX:InitialHeapSize=6442450944 
-XX:MaxHeapSize=6442450944 
-XX:MaxNewSize=2093797376 
-XX:NewSize=2093797376 
-XX:OldPLABSize=16 
-XX:OldSize=4187594752 
-XX:SurvivorRatio=8 
-XX:ThreadStackSize=256 
-XX:ParallelGCThreads=24 
-XX:+PrintGC 
-XX:+PrintGCDateStamps 
-XX:+PrintGCDetails 
-XX:+PrintGCTimeStamps 
-XX:+UseCMSCompactAtFullCollection
-XX:+UseCompressedClassPointers 
-XX:+UseCompressedOops 
-XX:+UseParNewGC 
-XX:+UseConcMarkSweepGC 
-XX:+UseFastAccessorMethods 

因为开发人员没有权限直接在线上操作JVM的相关工具命令,配置了默认从gc.log的开头能够看到该java应用程序进程的jvm虚拟机配置(如果有权限可以用jinfo + 进程号查看,一些未配置的默认参数可以通过java -XX:+PrintFlagsInitial命令查看到的本机参数):

  1. Java HotSpot™ 64-Bit Server VM (25.192-b12
    java虚拟机版本,不同版本的虚拟机的log可能有些名词与格 式不一定完全一致。

  2. JRE (1.8.0_192-b12)
    说明JDK版本是1.8
    JDK从1.8开始,去掉了永久代PermGen,被元空间Metaspace取代,(openJDK官方说明 link.)

  3. -XX:CMSFullGCsBeforeCompaction=3
    每三次FullGC整理一次内存碎片(感觉设的不是很合理)

  4. -XX:CMSInitiatingOccupancyFraction=70
    当老年已使用70%时开始CMS收集、预留30%给浮动垃圾

  5. -XX:GCTimeRatio=19
    GC时间占比不能超过 1 / (1 + 19) = 0.05
    link.

  6. -XX:+HeapDumpOnOutOfMemoryError
    出现堆内存溢出OutOfMemoryError错误时自动导出内存快照(这个参数很必要)

  7. -XX:InitialHeapSize=6442450944
    初始化堆内存6G

  8. -XX:MaxHeapSize=6442450944
    最大堆内存6G

  9. -XX:MaxNewSize=2093797376
    新生代2G

  10. -XX:OldPLABSize=16 老年代PLAB大小
    link.

  11. -XX:OldSize=4187594752
    老年代大小4G

  12. -XX:SurvivorRatio=8
    新生代eden: survivor from:survivor to = 8:1:1

  13. -XX:ThreadStackSize=256
    栈内存最大256K

  14. -XX:ParallelGCThreads=24
    开启24个并行GC线程(值得商榷)

  15. -XX:+PrintGCDetails
    打印gc详情

  16. -XX:+PrintGCTimeStamps
    打印gc时间戳(距离进程启动过去的秒数)

  17. -XX:+UseCMSCompactAtFullCollection
    CMS 回收完后进行内存碎片整理

  18. -XX:+UseCompressedClassPointers
    压缩类指针

  19. -XX:+UseCompressedOops
    JVM 会使用 32 位的 OOP, 压缩普通类指针

  20. -XX:+UseParNewGC
    新生代采用parallel并行收集器

  21. -XX:+UseConcMarkSweepGC
    老年代使用CMS收集器

  22. -XX:+UseFastAccessorMethods
    原始类型的快速优化 默认开启

  23. -XX:+PrintGCDateStamps
    输出gc时的日期时间

拷了一段线上的的gc.log

2020-02-15T21:36:05.196+0800: 3211179.532: [GC (Allocation Failure) 2020-02-15T21:36:05.196+0800: 3211179.532: [ParNew: 1780947K->121358K(1840256K), 0.01729
87 secs] 4746301K->3086712K(6087040K), 0.0177386 secs] [Times: user=0.34 sys=0.01, real=0.01 secs] 
2020-02-15T21:36:08.274+0800: 3211182.610: [GC (Allocation Failure) 2020-02-15T21:36:08.274+0800: 3211182.610: [ParNew: 1757198K->147711K(1840256K), 0.02898
96 secs] 4722552K->3113065K(6087040K), 0.0294950 secs] [Times: user=0.50 sys=0.00, real=0.03 secs] 
2020-02-15T21:36:11.409+0800: 3211185.745: [GC (Allocation Failure) 2020-02-15T21:36:11.409+0800: 3211185.745: [ParNew: 1783551K->135110K(1840256K), 0.02445
21 secs] 4748905K->3110389K(6087040K), 0.0249676 secs] [Times: user=0.41 sys=0.00, real=0.02 secs] 
2020-02-15T21:36:11.440+0800: 3211185.776: [GC (CMS Initial Mark) [1 CMS-initial-mark: 2975278K(4246784K)] 3110440K(6087040K), 0.0063124 secs] [Times: user=
0.09 sys=0.01, real=0.01 secs] 
2020-02-15T21:36:11.446+0800: 3211185.782: [CMS-concurrent-mark-start]
2020-02-15T21:36:11.545+0800: 3211185.882: [CMS-concurrent-mark: 0.099/0.099 secs] [Times: user=0.77 sys=0.02, real=0.09 secs] 
2020-02-15T21:36:11.546+0800: 3211185.882: [CMS-concurrent-preclean-start]
2020-02-15T21:36:11.561+0800: 3211185.897: [CMS-concurrent-preclean: 0.015/0.015 secs] [Times: user=0.05 sys=0.00, real=0.02 secs] 
2020-02-15T21:36:11.561+0800: 3211185.897: [CMS-concurrent-abortable-preclean-start]
2020-02-15T21:36:13.417+0800: 3211187.753: [CMS-concurrent-abortable-preclean: 1.848/1.856 secs] [Times: user=4.43 sys=0.26, real=1.85 secs] 
2020-02-15T21:36:13.423+0800: 3211187.759: [GC (CMS Final Remark) [YG occupancy: 1179300 K (1840256 K)]2020-02-15T21:36:13.423+0800: 3211187.759: [Rescan (p
arallel) , 0.0438625 secs]2020-02-15T21:36:13.467+0800: 3211187.803: [weak refs processing, 0.1168227 secs]2020-02-15T21:36:13.584+0800: 3211187.920: [class
 unloading, 0.0369863 secs]2020-02-15T21:36:13.621+0800: 3211187.957: [scrub symbol table, 0.0137513 secs]2020-02-15T21:36:13.635+0800: 3211187.971: [scrub 
string table, 0.0018793 secs][1 CMS-remark: 2975278K(4246784K)] 4154579K(6087040K), 0.2182690 secs] [Times: user=1.21 sys=0.01, real=0.22 secs] 
2020-02-15T21:36:13.642+0800: 3211187.978: [CMS-concurrent-sweep-start]
2020-02-15T21:36:14.479+0800: 3211188.815: [GC (Allocation Failure) 2020-02-15T21:36:14.479+0800: 3211188.815: [ParNew: 1770950K->153628K(1840256K), 0.02820
61 secs] 3416759K->1799437K(6087040K), 0.0286131 secs] [Times: user=0.59 sys=0.00, real=0.03 secs] 
2020-02-15T21:36:15.564+0800: 3211189.900: [CMS-concurrent-sweep: 1.870/1.922 secs] [Times: user=6.02 sys=0.36, real=1.92 secs] 
2020-02-15T21:36:15.564+0800: 3211189.900: [CMS-concurrent-reset-start]
2020-02-15T21:36:15.577+0800: 3211189.913: [CMS-concurrent-reset: 0.013/0.013 secs] [Times: user=0.03 sys=0.01, real=0.01 secs] 
2020-02-15T21:36:17.063+0800: 3211191.399: [GC (Allocation Failure) 2020-02-15T21:36:17.063+0800: 3211191.399: [ParNew: 1789468K->161577K(1840256K), 0.02158
78 secs] 2079683K->454921K(6087040K), 0.0219634 secs] [Times: user=0.46 sys=0.00, real=0.02 secs] 
2020-02-15T21:36:19.760+0800: 3211194.096: [GC (Allocation Failure) 2020-02-15T21:36:19.760+0800: 3211194.096: [ParNew: 1797417K->148849K(1840256K), 0.02010
22 secs] 2090761K->443504K(6087040K), 0.0205227 secs] [Times: user=0.44 sys=0.00, real=0.02 secs] 

二、新生代gc日志

2020-02-15T21:36:05.196+0800: 3211179.532: [GC (Allocation Failure) 2020-02-15T21:36:05.196+0800: 3211179.532: [ParNew: 1780947K->121358K(1840256K), 0.01729
87 secs] 4746301K->3086712K(6087040K), 0.0177386 secs] [Times: user=0.34 sys=0.01, real=0.01 secs] 

这是一行minorGC的日志,分析如下:

2020-02-15T21:36:05.196+0800:
这是gc发生的具体时间

3211179.532 :
从这个服务jvm进程启动那一刻起到本次gc发生时间隔的秒数

GC (Allocation Failure):发生GC的原因
没有MeteSpace、Full、CMS、System.gc()等关键字说明这只是新生代内存摆满,无法放下新对象而导致的一次的普通minorGC

[ParNew: 1780947K->121358K(1840256K), 0.0172987 secs]:
ParNew 说明新生代采用的是Parallel收集器
1780947K 本次minorGC前新生代已经使用的大小
121358K 回收完后剩余没回收对象大小
1840256K 新生代总可用空间(eden + 半个survivor区大小)
0.0172987 secs 本次新生代GC耗时0.0172987秒
4746301K 本次minorGC前堆内存总使用大小
3086712K 回收完后堆内存中剩余对象的大小
6087040K 整个jvm堆内存大小
0.0177386 secs 本次GC总耗时(括把年轻代的对象转移到老年代的时间和一些清零操作等)

[Times: user=0.34 sys=0.01, real=0.01 secs]
GC 不同维度耗时统计

‘user’ time is the CPU time spent in user-mode code (outside the kernel).
‘Sys’ time is the amount of CPU time spent in the kernel. This means executing CPU time spent in system calls within the kernel, as opposed to library code, which is still running in user-space.
‘real’ time is the total elapsed time of the GC event. This is basically the time that you see in the clock.

user=0.34:
用户态用时0.34秒,假如参与此次gc有多个CPU,则每个CPU耗时累加共计0.34秒
sys=0.01:
内核态耗时0.01秒
real=0.01 secs 本次GC实际耗时STW的时间0.01秒
Clock time for which your application was stopped. With Parallel GC this number should be close to (user time + system time) divided by the number of threads used by Garbage Collector. Note that due to some activities not being parallelizable, it always exceeds the ratio by a certain amount.

因为ParNew是多线程并行收集器,
user + sys ≈ real * 并行线程数
再加上一些锁竞争和不可并行的一些调度,一般user + sys 会大于 real * gc线程数。

但是有些异常情况下也有可能 real > user + sys
此时一般有2个原因:
1,磁盘IO负载过高,打印log阻塞时间过长
2,CPU负载太高,GC线程等待调度时间过长

gc对于我们系统吞吐性能的影响一般要关注的就是real大小

参考 link.

对本次gc的分析如下:
1,为何新生代总可用内存是1840256K?
新生代总大小 -XX:NewSize=2093797376 / 1024 = 2044724K
-XX:SurvivorRatio=8 所以
eden = 2044724K* 0.8
survivor = 2044724K* 0.2
新生代是复制算法所以survivor 有一半不能使用
因此新生代总的可用内存是eden + survivor * 1/2 = 2044724K * 0.9 = 1840251.6K

1780947K->121358K 说明本次minorGC
回收掉了1780947K - 121358K = 1659589K的垃圾对象
剩余121358K没回收掉的在survivor的to区

一次minorGC回收掉了新生代中93%的对象 耗时10毫秒
还算正常

4746301K->3086712K 说明整个堆内存被占用3086712K
因此老年代此时有3086712K - 121358K = 2965354K

老年代总共是 6442450944 - 2093797376 = 4246732K

因为-XX:CMSInitiatingOccupancyFraction=70 所以当老年代使用达到70%时就会触发old gc

4246732K * 70% = 2972712.4K - 2965354K = 7358.4K
可知,再有7358K进入老年代,即将触发oldgc

2020-02-15T21:36:11.409+0800: 3211185.745: [GC (Allocation Failure) 2020-02-15T21:36:11.409+0800: 3211185.745: [ParNew: 1783551K->135110K(1840256K), 0.0244521 secs] 
 4748905K->3110389K(6087040K), 0.0249676 secs] [Times: user=0.41 sys=0.00, real=0.02 secs]

注意在这次minorGC中,
新生代总共回收 1783551K->135110K = 1648441K
堆内存总占用却只减少了 4748905K->3110389K = 1638516K
也就是有1648441K- 1638516K = 9925K
=9.7M新生代对象进入了老年代。

从 2020-02-15T21:36:05.196+0800:
到 2020-02-15T21:36:11.440+0800:
过了6秒后 果然发生了CMS的老年代gc

三、老年代gc日志

启动参数设置的 -XX:+UseConcMarkSweepGC 所以老年代是Concurrent Mark and Sweep收集器,简称CMS,分为7个步骤。
以前很多博文说发生老年代gc是stop the world的,准确来说。
只有2个步骤是stop the world的

3.1、 CMS Initial Mark - 初始标记阶段

标记老年代中所有的GC Roots;二是标记被年轻代中活着的对象引用的对象,== 本阶段是stop the world的 ==

2020-02-15T21:36:11.440+0800: 3211185.776: [GC (CMS Initial Mark) [1 CMS-initial-mark: 2975278K(4246784K)] 3110440K(6087040K), 0.0063124 secs] [Times: user=
0.09 sys=0.01, real=0.01 secs]

CMS Initial Mark:初始标记阶段
2975278K: 老年代已经使用
4246784K: 老年代总空间
3110440K: 堆内存已使用大小
6087040K: 整个堆内存大小
real=0.01 secs:实际耗时

因为本次只标记被GC root直接引用的对象,量很小,stop the world 停顿了大概10毫秒 与一次minorGC耗时差不多

3.2 CMS-concurrent-mark 并发标记阶段

从“初始化标记”阶段找到的GC Roots开始,遍历整个老年代并且标记所有可达的存活对象,本阶段应用程序线程同时运行。
因为标记的同时应用程序会改变一些对象的引用等,并不是老年代的所有存活对象都会被标记,也有可能刚刚标记完存活的对象,又失去了引用。在此期间,凡是有引用改变的对象,它所在的内存区域就会被标为一个“card” 区域,这些会在下一阶段处理。(Whenever that happens, the JVM marks the area of the heap (called “Card”) that contains the mutated object as “dirty” (this is known as Card Marking).)

2020-02-15T21:36:11.545+0800: 3211185.882: [CMS-concurrent-mark: 0.099/0.099 secs] [Times: user=0.77 sys=0.02, real=0.09 secs]

0.099/0.099 secs 并行标记耗时,应该是2位小数截断 real=0.09 secs,此阶段耗时接近100ms,但是应用程序也在并行,不会真的卡顿100ms,不过gc的线程会和应用线程竞争CPU资源,如果CPU整体使用率很高则也会对性能造成影响。

3.3 CMS-concurrent-preclean 预清理阶段

(个人觉得这个翻译成清理预备阶段更合适)
在上一阶段的并发标记期间,一些对象的引用已经发生了变化,当这些引用发生变化的时候,JVM会标记堆的这个区域为Dirty Card(包含被标记但是改变了的对象,被认为"dirty")

在pre-clean阶段,那些能够从dirty card对象到达的对象也会被标记,这个标记做完之后,dirty card标记就会被清除了,还会做一些final remark阶段需要的准备工作。

预清理阶段可以减少下一个stop-the-world 重新标记阶段的工作量,,这个阶段也是和应用程序并行的

2020-02-15T21:36:11.561+0800: 3211185.897: [CMS-concurrent-preclean: 0.015/0.015 secs] [Times: user=0.05 sys=0.00, real=0.02 secs] 

pre-clean阶段负责前一个阶段标记了又发生改变的对象标记,耗时约20ms

3.4 CMS-concurrent-abortable-preclean

可中断预清理阶段,这个阶段也是可以并行的。它会一直不断重复地做类似上一阶段的预清理标记工作,直到被某个条件打断为止,比如:
1、如果设置了-XX:CMSMaxAbortablePrecleanTime=3000 (默认值5s),代表该阶段最大的持续时间 那么3S这个阶段就会打断结束。
2、-XX:CMSScheduleRemarkEdenPenetration=40 ,默认值50%,代表Eden区使用比例超过40%就结束该阶段进入remark

这个阶段也是为了减少下一个remark阶段重新标记阶段的工作量,可以控制这个阶段的结束时机,尽量减少remark阶段的STW。

2020-02-15T21:36:13.417+0800: 3211187.753: [CMS-concurrent-abortable-preclean: 1.848/1.856 secs] [Times: user=4.43 sys=0.26, real=1.85 secs] 

real=1.85 secs 持续了1.85秒达到了某个终止条件,默认参数情况下最多可以持续5s。

3.5 Final Remark 最终标记阶段

这个阶段是stop the world的。该阶段的任务是完成标记整个年老代的所有的存活对象。由于之前的预处理是并发的,它可能跟不上应用程序改变的速度。这个阶段会标记老年代全部的存活对象,包括那些在并发标记阶段更改的或者新创建的引用对象;

2020-02-15T21:36:13.423+0800: 3211187.759: [GC (CMS Final Remark) [YG occupancy: 1179300 K (1840256 K)]2020-02-15T21:36:13.423+0800: 3211187.759: [Rescan (p
arallel) , 0.0438625 secs]2020-02-15T21:36:13.467+0800: 3211187.803: [weak refs processing, 0.1168227 secs]2020-02-15T21:36:13.584+0800: 3211187.920: [class
 unloading, 0.0369863 secs]2020-02-15T21:36:13.621+0800: 3211187.957: [scrub symbol table, 0.0137513 secs]2020-02-15T21:36:13.635+0800: 3211187.971: [scrub 
string table, 0.0018793 secs][1 CMS-remark: 2975278K(4246784K)] 4154579K(6087040K), 0.2182690 secs] [Times: user=1.21 sys=0.01, real=0.22 secs] 

GC (CMS Final Remark) :表示这是CMS的重新标记阶段,会STW,该阶段的任务是完成标记整个年老代的所有的存活对象

YG occupancy: 1179300 K (1840256 K) :新生代使用量(新生代总大小),使用率约64%,通常Final Remark阶段要尽量运行在年轻代是足够干净的时候,这样可以减少紧接着的连续的几个STW阶段。

[Rescan (parallel) , 0.0438625 secs] : 该阶段会重新扫描CMS堆中剩余的对象,重新从“根对象”开始扫描,并且也会处理对象关联本次扫描共耗时 0.0438625秒。这是整个final remark阶段扫描对象的用时总计,

[weak refs processing, 0.1168227 secs] : secs]第一个子阶段,表示对弱引用(比如ThreadLocal)的处理耗时为 0.1168227 secs,
消耗100多毫秒

[class unloading, 0.0369863 secs] 第二个子阶段,表示卸载无用的类的耗时为0.0369863 秒

[scrub symbol table, 0.0137513 secs] 最后子阶段,清理分别包含类级元数据和内部化字符串的符号和字符串表

[1 CMS-remark: 2975278K(4246784K)] 4154579K(6087040K), 0.2182690 secs] :
经历了上面的阶段后:
2975278K(4246784K):老年代内存已使用(老年代总大小)
4154579K(6087040K):堆内存已使用(堆内存总大小)
0.2182690 secs: 整个final remark的耗时,200多毫秒停顿

3.6 CMS-concurrent-sweep: 并发清除阶段

CMS-remark阶段标记了老年代的可达的对象,不可达的就被标记为垃圾对象,这个阶段就是把之前remark标记阶段没有被标记可达的垃圾对象清除掉,并回收内存。该阶段和用户线程并发执行,不会STW。
[CMS-concurrent-sweep: 1.870/1.922 secs] 清除那些没有标记的无用对象并回收内存,耗时1.922秒

3.7 CMS-concurrent-reset 重置阶段

[CMS-concurrent-reset: 0.013/0.013 secs] 重新设置CMS算法内部的数据结构,准备下一个CMS生命周期的使用,耗时0.013秒。

另外注意3211188.815: 的时候,在并发清理阶段,有一次 [GC (Allocation Failure)。

2020-02-15T21:36:13.642+0800: 3211187.978: [CMS-concurrent-sweep-start]
2020-02-15T21:36:14.479+0800: 3211188.815: [GC (Allocation Failure) 2020-02-15T21:36:14.479+0800: 3211188.815: [ParNew: 1770950K->153628K(1840256K), 0.02820
61 secs] 3416759K->1799437K(6087040K), 0.0286131 secs] [Times: user=0.59 sys=0.00, real=0.03 secs]

老年代CMS期间,只要不是STW的时候,和新生代的minorGC同时发生,这个是没毛病的。由此产生的就是浮动垃圾。如果在CMS老年代回收还没完成的时候,新生代再次发生minorGC时如果有存活对象进入老年代,而老年代没有预留足够空间摆下,就会发生“Concurrent Mode Failure”失败,此时虚拟机就会启用Serial Old单线程收集器来重新进行老年代的垃圾收集,这样停顿时间就会很长。这也是为什么设置了-XX:CMSInitiatingOccupancyFraction=70 预留了30%的空间给浮动垃圾。

2020-02-15T21:36:17.063+0800: 3211191.399: [GC (Allocation Failure) 2020-02-15T21:36:17.063+0800: 3211191.399: [ParNew: 1789468K->161577K(1840256K), 0.02158
78 secs] 2079683K->454921K(6087040K), 0.0219634 secs] [Times: user=0.46 sys=0.00, real=0.02 secs]

从CMS-concurrent-sweep完成后第一行日志来看,新生代占用161577K,整个堆内存占用454921K 也就是此时老年代中有454921K - 161577K = 293344K

对比CMS回收老年代前2975278K - 293344K = 2681934K
大致可以推断== 回收了2681934K的老年代空间 ==

总体来看,本次old GC从3211185.776秒开始 到3211189.913结束,总共持续了3211189.913 - 3211185.776 = 4.137秒。
但是 4.137秒 中只有初始标记阶段和重新标记阶段是stop the world的,这2个阶段停顿了200多毫秒的卡顿。

总结

从gc日志,我们可以对该服务得出如下结论:

1、 目前日活1000万,高峰期间隔3秒左右发生一次minorGC,2G*0.8=1.6G的Eden区,说明这个服务高峰期内存消耗速度每秒需要500多M。每次 minorGC能腾出回收掉90%以上的对象,停顿20ms。如果日活增长或者push集中,新生代空间消耗太快,minorGC会有些太频繁。

2、这个服务线上每天OldGC发生大约7次,
一次oldGC后老年代剩余293344K,每次oldGC后剩余的老年代对象占老年代空间的大约7% 说明并不存在明显内存泄漏 问题。

3、老年代回收不掉的长期对象不到300M,说明绝大部分是“短命”对象。这种“短命”系统,6G的内存却分配了4G给老年代,有点不合理。

4、每次oldGC的停顿STW时间 200ms+ 如果有些接口超时时间设置的低于200ms,那每天都会有一定数量的超时。这里有一定的调优空间。

针对以上根据GC日志总结的现状,需要对这个系统进行一些调优。待线上验证效果后再来总结。

参考:

gceasy.io 一个gc日志在线分析网站
https://blog.gceasy.io/2016/04/06/gc-logging-user-sys-real-which-time-to-use/ GC三种维度的times统计

plumbr官网,国内很多gc技术文章的插图基本都摘自这里
https://plumbr.io/handbook/garbage-collection-algorithms-implementations
https://plumbr.io/handbook/gc-tuning-measuring#gc-logs
http://downloads.plumbr.eu/Plumbr%20Handbook%20Java%20Garbage%20Collection.pdf

Oracle官方文档
https://docs.oracle.com/javase/8/docs/technotes/guides/vm/gctuning/cms.html

https://blogs.oracle.com/poonam/understanding-cms-gc-logs

https://docs.oracle.com/javase/8/docs/technotes/tools/unix/java.html?utm_source=hacpai.com#BABFAFAE

https://docs.oracle.com/javase/8/docs/technotes/tools/unix/java.html

https://docs.oracle.com/javase/8/docs/technotes/guides/vm/gctuning/cms.html

一些博客

https://blog.csdn.net/flysqrlboy/article/details/88679457 这是一次对根据gc日志对concurrent-abortable-preclean这个阶段的一次调优,比较少见

https://www.jianshu.com/p/ba768d8e9fec
https://www.cnblogs.com/zhangxiaoguang/p/5792468.html
https://blog.csdn.net/BDX_Hadoop_Opt/article/details/38021209
https://blog.csdn.net/snowy_sakura/article/details/8455502
https://www.jianshu.com/p/ba768d8e9fec
https://www.cnblogs.com/zhangxiaoguang/p/5792468.html

  • 0
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值