java 垃圾回收机制 以及垃圾回收机制的触发

时间:2022-12-20 00:02:29
本篇文章的重点以转载为主 后面有自己的分析 有理解不到位的地方 期待与各位交流

以下内容转载自 : https://www.zhihu.com/question/41922036/answer/93079526

1. Full GC定义是相对明确的,就是针对整个新生代、老生代、元空间(metaspace,java8以上版本取代perm gen)的全局范围的GC;
2. Minor GC和Major GC是俗称,在Hotspot JVM实现的Serial GC, Parallel GC, CMS, G1 GC中大致可以对应到某个Young GC和Old GC算法组合;
3. 最重要是搞明白上述Hotspot JVM实现中几种GC算法组合到底包含了什么。
3.1 Serial GC算法:Serial Young GC + Serial Old GC (敲黑板!敲黑板!敲黑板!实际上它是全局范围的Full GC);
3.2 Parallel GC算法:Parallel Young GC + 非并行的PS MarkSweep GC / 并行的Parallel Old GC(敲黑板!敲黑板!敲黑板!这俩实际上也是全局范围的Full GC),选PS MarkSweep GC 还是 Parallel Old GC 由参数UseParallelOldGC来控制;
3.3 CMS算法:ParNew(Young)GC + CMS(Old)GC (piggyback on ParNew的结果/老生代存活下来的object只做记录,不做compaction)+ Full GC for CMS算法(应对核心的CMS GC某些时候的不赶趟,开销很大);
3.4 G1 GC:Young GC + mixed GC(新生代,再加上部分老生代)+ Full GC for G1 GC算法(应对G1 GC算法某些时候的不赶趟,开销很大);
4. 搞清楚了上面这些组合,我们再来看看各类GC算法的触发条件。
简单说,触发条件就是某GC算法对应区域满了,或是预测快满了。比如,
4.1 各种Young GC的触发原因都是eden区满了;
4.2 Serial Old GC/PS MarkSweep GC/Parallel Old GC的触发则是在要执行Young GC时候预测其promote的object的总size超过老生代剩余size;
4.3 CMS GC的initial marking的触发条件是老生代使用比率超过某值;
4.4 G1 GC的initial marking的触发条件是Heap使用比率超过某值,跟4.3 heuristics 类似;
4.5 Full GC for CMS算法和Full GC for G1 GC算法的触发原因很明显,就是4.3 和 4.4 的fancy算法不赶趟了,只能全局范围大搞一次GC了(相信我,这很慢!这很慢!这很慢!);
5 题主说的 “Full GC会先触发一次Minor GC” - 指的应该是
5.1 (说错了,我删了)
5.2 PS MarkSweep GC/Parallel Old GC(Full GC)之前会跑一次Parallel Young GC;
原因就是减轻Full GC 的负担。

5.2 -> ps

当准备要触发一次young GC时,如果发现统计数据说之前young GC的平均晋升大小比目前old gen剩余的空间大,则不会触发young GC而是转为触发full GC(因为HotSpot VM的GC里,除了CMS的concurrent collection之外,其它能收集old gen的GC都会同时收集整个GC堆,包括young gen,所以不需要事先触发一次单独的young GC);或者,如果有perm gen的话,要在perm gen分配空间但已经没有足够空间时,也要触发一次full GC;或者System.gc()、heap dump带GC,默认也是触发full GC。

以上内容转载自 : https://www.zhihu.com/question/41922036/answer/93079526


重点提一下 cms gc 的重要参数

cms

-XX:CMSInitiatingOccupancyFraction=80  设置cms cycle trigger 的阈值 默认-1 <占用 老年带的大小>
-XX:+UseCMSInitiatingOccupancyOnly
g1
-XX:InitiatingHeapOccupancyPercent=80  设置concurrent GC cycle trigger <默认 45% ,占用整个heap的大小>


以下不是转载 是自己结合生产实践 自己的理解

jvm hotspot中包含的所有的垃圾回收器   串行 并行 并发 g1
连线的表示可以搭配使用
●并行(Parallel):  指多条垃圾收集线程并行工作,但此时用户线程仍然处于等待状态。
●并发(Concurrent):指用户线程与垃圾收集线程同时执行(但不一定是并行的,可能会交替执行),用户程序在继续运行,而垃圾收集程序运行于另一个CPU上。
新生代
1. Serial收集器 <单线程> [串行]
2. ParNew收集器 (serial 的多线程版本) [并行]
3. Parallel Scavenge收集器<复制算法, 并行多线程收集器> [并行]
老年代
1. Serial Old收集器 <serial 收集器的老年代版本>  [串行]
2. Parallel Old收集器 <Parallel Scavenge收集器的老年代版本, 标记-整理> [并行]
3. CMS <获取最短回收停顿时间为目标的收集器,  标记—清除>   [并发]
ps: 5个过程  2个stw
1. 初始标记(stw) 
2. 并发标记 
3. 并发预清理
4. <最终标记>重标记(stw) 
5. 并发清理 
6. 重置  
g1 垃圾回收器
G1提供了两种GC模式,Young GC和Mixed GC,两种都是Stop The World(STW)

我所在公司g1 垃圾收集器的gclog

{Heap before GC invocations=3573 (full 90):
garbage-first heap total 4538368K, used 3325904K [0x00000006ab000000, 0x00000006ac0008a8, 0x00000007c0000000)
region size 16384K, 172 young (2818048K), 9 survivors (147456K)
Metaspace used 163630K, capacity 188987K, committed 192256K, reserved 1220608K
class space used 16357K, capacity 20273K, committed 20992K, reserved 1048576K
2018-03-25T00:25:58.605+0800: 224650.575: [GC pause (G1 Evacuation Pause) (young)
Desired survivor size 184549376 bytes, new threshold 15 (max 15)
- age 1: 17013632 bytes, 17013632 total
- age 2: 13209056 bytes, 30222688 total
- age 3: 14502912 bytes, 44725600 total
- age 4: 15785216 bytes, 60510816 total
- age 5: 14071128 bytes, 74581944 total
- age 6: 13162464 bytes, 87744408 total
- age 7: 12541320 bytes, 100285728 total
224650.575: [G1Ergonomics (CSet Construction) start choosing CSet, _pending_cards: 23840, predicted base time: 24.30 ms, remaining time: 175.70 ms, target pause time: 200.00 ms]
224650.575: [G1Ergonomics (CSet Construction) add young regions to CSet, eden: 163 regions, survivors: 9 regions, predicted young region time: 36.08 ms]
224650.575: [G1Ergonomics (CSet Construction) finish choosing CSet, eden: 163 regions, survivors: 9 regions, old: 0 regions, predicted pause time: 60.38 ms, target pause time: 200.00 ms]
, 0.0731631 secs]
[Parallel Time: 68.3 ms, GC Workers: 4]
[GC Worker Start (ms): Min: 224650575.6, Avg: 224650575.6, Max: 224650575.6, Diff: 0.1]
[Ext Root Scanning (ms): Min: 2.9, Avg: 3.2, Max: 3.8, Diff: 0.9, Sum: 13.0]
[Update RS (ms): Min: 8.7, Avg: 8.9, Max: 9.0, Diff: 0.3, Sum: 35.5]
[Processed Buffers: Min: 43, Avg: 54.2, Max: 62, Diff: 19, Sum: 217]
[Scan RS (ms): Min: 0.0, Avg: 0.0, Max: 0.1, Diff: 0.0, Sum: 0.2]
[Code Root Scanning (ms): Min: 0.0, Avg: 0.0, Max: 0.1, Diff: 0.1, Sum: 0.1]
[Object Copy (ms): Min: 55.6, Avg: 55.9, Max: 56.1, Diff: 0.6, Sum: 223.5]
[Termination (ms): Min: 0.0, Avg: 0.0, Max: 0.0, Diff: 0.0, Sum: 0.0]
[Termination Attempts: Min: 1, Avg: 1.0, Max: 1, Diff: 0, Sum: 4]
[GC Worker Other (ms): Min: 0.0, Avg: 0.0, Max: 0.0, Diff: 0.0, Sum: 0.1]
[GC Worker Total (ms): Min: 68.1, Avg: 68.1, Max: 68.1, Diff: 0.1, Sum: 272.5]
[GC Worker End (ms): Min: 224650643.7, Avg: 224650643.7, Max: 224650643.7, Diff: 0.0]
[Code Root Fixup: 0.3 ms]
[Code Root Purge: 0.0 ms]
[Clear CT: 0.3 ms]
[Other: 4.3 ms]
[Choose CSet: 0.0 ms]
[Ref Proc: 3.1 ms]
[Ref Enq: 0.1 ms]
[Redirty Cards: 0.4 ms]
[ Humongous Register: 0.0 ms]
[ Humongous Reclaim: 0.0 ms]
[Free CSet: 0.2 ms]
[Eden: 2608.0M(2608.0M)->0.0B(2592.0M) Survivors: 144.0M->160.0M Heap: 3248.0M(4432.0M)->664.0M(4432.0M)]
Heap after GC invocations=3574 (full 90):
garbage-first heap total 4538368K, used 679888K [0x00000006ab000000, 0x00000006ac0008a8, 0x00000007c0000000)
region size 16384K, 10 young (163840K), 10 survivors (163840K)
Metaspace used 163630K, capacity 188987K, committed 192256K, reserved 1220608K
class space used 16357K, capacity 20273K, committed 20992K, reserved 1048576K
}
[Times: user=0.28 sys=0.00, real=0.07 secs]

mixed GC
  1. 全局并发标记(global concurrent marking)
  2. 拷贝存活对象(evacuation)
自己的理解 <g1>
1. gc 会对eden 和 surivor 重新分配   回收内存 提高内存利用率  region [1-32m] 代码写死的最大2048个region
2. target pause time: 200ms  这个是g1 的默认配置  [-XX:MaxGCPauseMillis=time]
3. gc 可以对垃圾回收进行预测
4. ygc 和 mixgc 都是会stw
5. 尽量避免fullgc 产生 < 某些情况下 这会导致Serial gc,很慢 很慢>

6. 整个内存分为 E V H<巨型区域,放大对象的> O 

以下g1 触发gc的条件转自: http://blog.jobbole.com/109170/

触发Full GC

在某些情况下,G1触发了Full GC,这时G1会退化使用Serial收集器来完成垃圾的清理工作,它仅仅使用单线程来完成GC工作,GC暂停时间将达到秒级别的。整个应用处于假死状态,不能处理任何请求,我们的程序当然不希望看到这些。那么发生Full GC的情况有哪些呢?

  • 并发模式失败

G1启动标记周期,但在Mix GC之前,老年代就被填满,这时候G1会放弃标记周期。这种情形下,需要增加堆大小,或者调整周期(例如增加线程数-XX:ConcGCThreads等)。

  • 晋升失败或者疏散失败

G1在进行GC的时候没有足够的内存供存活对象或晋升对象使用,由此触发了Full GC。可以在日志中看到(to-space exhausted)或者(to-space overflow)。解决这种问题的方式是:

a,增加 -XX:G1ReservePercent 选项的值(并相应增加总的堆大小),为“目标空间”增加预留内存量。

b,通过减少 -XX:InitiatingHeapOccupancyPercent 提前启动标记周期。

c,也可以通过增加 -XX:ConcGCThreads 选项的值来增加并行标记线程的数目。

  • 巨型对象分配失败

当巨型对象找不到合适的空间进行分配时,就会启动Full GC,来释放空间。这种情况下,应该避免分配大量的巨型对象,增加内存或者增大-XX:G1HeapRegionSize,使巨型对象不再是巨型对象。