G1垃圾回收器

542次阅读
没有评论

共计 13164 个字符,预计需要花费 33 分钟才能阅读完成。

 

概述

  • 官网

https://docs.oracle.com/en/java/javase/16/gctuning/garbage-first-g1-garbage-collector1.html#GUID-ED3AB6D3-FD9B-4447-9EDF-983ED2F7A573

  • 简介

垃圾回收 G1 垃圾回收器 可以同时回收新生代和老年代的对象,不需要两个 垃圾回收 器 配合起来运作,他一个人就可以搞定所有的垃圾回收。他最大的特点,就是把Java堆内存拆分为多个大小相等的Region以及设置一个垃圾回收的预期停顿时间。G1也会有新生代和老年代的概念,但是只不过是逻辑上的概念,但是在同一时刻只能属于某个代,G1还提供了专门的Region来存放大对象,而不是让大对象进入老年代的Region中。

G1垃圾回收器

垃圾回收

Region

垃圾回收 G1将划分为多个大小相等的Region。每个小堆区都可能是 Eden区,Survivor区或者Old区,但是在同一时刻只能属于某个区在逻辑上,所有的Eden区和Survivor区合起来就是新生代,所有的Old区合起来就是老年代,且新生代和老年代各自的内存Region区域由G1自动控制,不断变动。JVM最多可以有2048个Region,每个Region的大小在1-32MB之间,具体多大取决于堆的大小。

巨型对象

垃圾回收
一个大对象超过了一个Region大小的50%,就会被放入大对象专门的Region中(Humongous区域),而且一个大对象如果太大,可能会横跨多个Region来存放。大对象既不属于新生代也不属于老年代。在新生代、老年代在回收垃圾的时候,会顺带带着大对象Region一起回收

对象进入老年代

  1. 对象在新生代躲过了很多次的垃圾回收,达到了一定的年龄了,

-XX:MaxTenuringThreshold参数可以设置这个年龄,他就会进入老年代

  1. 动态年龄判定规则,如果一旦发现某次新生代GC过后,存活对象超过了Survivor的50%

G1特点

  1. 与应用线程同时工作,几乎不需要STW;
  2. 整理剩余空间,不产生内存碎片(G1采用复制算法,CMS只能在FULL GC时,用STW整理内存碎片)
  3. GC停顿更加可控
  4. 不牺牲系统的吞吐量
  5. GC不要求额外的内存空间(CMS需要预留空间存储浮动垃圾)
  6. 对每个角色的数量并没有强制的限定,对每种内存的大小,可以动态变化。
  7. GC停顿时间会明显变少(G1只是特定的整理几个Region。并不是整理所有的Region)

G1重要概念

  1. 分区:对应CMS的card page。每一个区域的大小是一样的。但是角色会动态发生变化。但是在同一时刻,某一个分区只是属于单一的某一个代
  2. 在物理上内存不需要连续,好处就是有的分区内存垃圾对象多,有的分区垃圾少,G1会有限回收垃圾多的分区
  3. 在新生代满的时候,对新生代进行回收。和传统的一致。
  4. 收集集合(CSet):记录了GC要收集的Regions集合,CSet包括需要收集的Eden Regions、Survivor Regions,而且还包括部分(1/8)Old Regions(混合收集的时候,收集年轻代的时候,会收集一部分老年代的Region)
  5. Remembered Set:为了解决跨代引用关系,例如老年代引用了新生代的对象,YGC时如果要扫描整个老年代代价太大了,所以引入了Rset。每一个Region都对应一个RSet,RSet保存了来自其他Region内的对象对于此分段的引用。对于属于年轻代的Region(Eden和Survivor区的内存分段)来说,RSet只保存来自老年代的对象的引用。这是因为年轻代回收是针对全部年轻代的对象的,反正所有年轻代内部的对象引用关系都会被扫描,所以RSet不需要保存来自年轻代内部的引用。对于属于老年代分段的RS来说,也只会保存来自老年代的引用,这是因为老年代的回收之前会先进行年轻代的回收,年轻代回收后Eden区变空了,G1会在老年代回收过程中扫描Survivor区到老年代的引用。

RSet集合用来记录并跟踪其它Region指向该Region中对象的引用,每个Region默认按照512Kb划分成多个Card,所以RSet需要记录的东西应该是 xxRegion的 xx Card。 RSet的数据结构是一个Hash Table,Key是别的Region的起始地址,Value是一个集合,里面的元素是Card Table的Index。

G1垃圾回收器

垃圾回收

G1工作原理

每次GC不必都去处理整个堆空间,而是每次只处理一部分Region,实现大容量内存的GC,通过计算每个Region的回收价值,包括回收所需时间、可回收空间,在有限时间内尽可能回收更多的内存,把垃圾回收造成的停顿时间控制在预期配置的时间范围内。
在逻辑上,G1分为年轻代和老年代,但是它的比例并不是那么”固定”,为了达到MaxGCPauseMillis所规定的效果,G1会自动调整两者之间的比例。如果强行使用-Xmn或者-XX:NewRatio去设定它们的比例的话,则给G1设定的这个目标MaxGCPauseMilli将会失效。
G1的回收过程主要分为2类,在Young GC触发的时候会伴随着全局并发标记,他主要是为Mixd GC提供标记服务的

  1. G1年轻代的垃圾回收,同样叫Young GC ,这个过程和ParNew的类似,发生时机就是Eden区满的时候(Eden区什么时候满不仅仅受-XX:G1MaxNewSizePercent参数影响,G1会根据-XX:MaxGCPauseMills参数调整GC时机,详细参考案例1)。新生代的垃圾回收还是采用了复制算法,只不过会考虑预设GC停顿时间,保证垃圾回收的停顿时间不能超过预设时间,因此会挑选一些Region来进行垃圾回收
  2. 混合模式,也叫Mixed GCMixed GC 不是FULL GC,如果Mixed GC 实在无法跟上主程序分配内存的速度,导致老年代填满无法继续执行Mixed GC,,就会使用serial Old GC(FULL GC)来收集整个堆,本质上G1不提供FULL GC)Mixed GC不只清理年轻代,还会将老年代的一部分区域进行清理。G1有一个参数,是**-XX:InitiatingHeapOccupancyPercent**,他的默认值是45%意思就是说,如果老年代占据了堆内存的45%的Region的时候,此时就会尝试触发一个新生代+老年代一起回收的混合回收阶段

年轻代回收过程

    • 并行的、独占式的(STW发生)的垃圾回收,可能会发生对象的代晋升,将会把对象放入Survivor或者是老年代。
    • 当Eden区内存空间耗尽的时候,G1会启动一次年轻代垃圾回收,顺带回收Survivor区。
      • G1 Young GC的时候,首先停止程序线程(STW),G1创建回收集(Collection set,需要被回收的内存分段集合,包括Eden区和Survivor区的所有内存分段)
      • 第一阶段:扫描根(GC Roots),GC Roots联通RSet记录的外部引用作为扫描存活对象的入口。
      • 第二阶段:处理dirty card queue中的card,更新RSet。此阶段完成后,RSet可以准确的反应老年代对所在的内存分段中对象的引用
      • 第三阶段:识别被老年代对象指向的Eden区中的对象,这些被指向的Eden中对象被认为是存活的对象。
      • 第四阶段:复制对象(使用的是复制算法),对象数被遍历,Eden区region中存活的对象会被复制到Survivor区中的region,Survivor region中的存活对象如果年龄未达到阈值,年龄+1,达到阈值会被复制到Old region,如果Survivior的空间不够用,Eden中的部分数据会直接晋升到老年代。
      • 第五阶段:处理Soft、Weak、Phantom、Final、JNI Weak等引用,最终Eden区的数据为空,这些空的region将会等待对象的分配,GC停止工作,目标内存中的对象也都是连续的,没有内存碎片。
    • YGC日志
2021-09-02T14:37:46.816+0800: 1.095: [GC pause (G1 Evacuation Pause) (young)
Desired survivor size 1572864 bytes, new threshold 15 (max 15)
, 0.0112287 secs]
[Parallel Time: 6.0 ms, GC Workers: 8]
[GC Worker Start (ms): Min: 1095.4, Avg: 1096.3, Max: 1097.1, Diff: 1.7]
[Ext Root Scanning (ms): Min: 0.0, Avg: 0.7, Max: 2.0, Diff: 2.0, Sum: 5.3]
[Update RS (ms): Min: 0.0, Avg: 0.0, Max: 0.0, Diff: 0.0, Sum: 0.0]
[Processed Buffers: Min: 0, Avg: 0.0, Max: 0, Diff: 0, Sum: 0]
[Scan RS (ms): Min: 0.0, Avg: 0.0, Max: 0.0, Diff: 0.0, Sum: 0.1]
[Code Root Scanning (ms): Min: 0.0, Avg: 0.6, Max: 3.1, Diff: 3.1, Sum: 5.1]
[Object Copy (ms): Min: 0.3, Avg: 2.3, Max: 3.2, Diff: 2.9, Sum: 18.7]
[Termination (ms): Min: 0.0, Avg: 0.5, Max: 1.3, Diff: 1.3, Sum: 4.0]
[Termination Attempts: Min: 1, Avg: 3.0, Max: 5, Diff: 4, Sum: 24]
[GC Worker Other (ms): Min: 0.0, Avg: 0.0, Max: 0.1, Diff: 0.0, Sum: 0.4]
[GC Worker Total (ms): Min: 3.3, Avg: 4.2, Max: 5.0, Diff: 1.7, Sum: 33.6]
[GC Worker End (ms): Min: 1100.4, Avg: 1100.5, Max: 1101.1, Diff: 0.7]
[Code Root Fixup: 0.4 ms]
[Code Root Purge: 0.0 ms]
[Clear CT: 1.0 ms]
[Other: 3.8 ms]
[Choose CSet: 0.0 ms]
[Ref Proc: 1.9 ms]
[Ref Enq: 0.0 ms]
[Redirty Cards: 1.4 ms]
[Humongous Register: 0.0 ms]
[Humongous Reclaim: 0.0 ms]
[Free CSet: 0.1 ms]
[Eden: 24.0M(24.0M)->0.0B(39.0M) Survivors: 0.0B->3072.0K Heap: 24.0M(128.0M)->2331.1K(128.0M)]
[Times: user=0.03 sys=0.01, real=0.01 secs]

全局并发标记

通过-XX:InitiatingHeapOccupancyPercent指定触发全局并发标记的老年代使用占比,默认值45%,也就是老年代占堆的比例超过45%。如果Mixed GC周期结束后老年代使用率还是超过45%,那么会再次触发全局并发标记过程。
全局并发标记主要是为Mixed GC计算找出回收收益较高的Region区域,具体分为5个阶段

  1. 初始标记:初始标记会产生STW,它标记了从GC Root开始直接可达的对象,这个过程会执行一次YGC。下面是一个日志示例:
2021-09-02T14:37:51.686+0800: 5.965: [GC pause (Metadata GC Threshold) (young) (initial-mark)
Desired survivor size 5242880 bytes, new threshold 15 (max 15)
- age   1:     288648 bytes,     288648 total
- age   2:     302552 bytes,     591200 total
- age   3:     216632 bytes,     807832 total
- age   4:     350720 bytes,    1158552 total
- age   5:     455736 bytes,    1614288 total
- age   6:     452712 bytes,    2067000 total
- age   7:    1188192 bytes,    3255192 total
- age   8:     613664 bytes,    3868856 total
- age   9:     322392 bytes,    4191248 total
, 0.0200902 secs]
[Parallel Time: 15.2 ms, GC Workers: 8]
[GC Worker Start (ms): Min: 5965.5, Avg: 5966.5, Max: 5967.5, Diff: 1.9]
[Ext Root Scanning (ms): Min: 2.5, Avg: 4.4, Max: 13.0, Diff: 10.5, Sum: 35.0]
[Update RS (ms): Min: 0.0, Avg: 0.2, Max: 0.6, Diff: 0.6, Sum: 1.7]
[Processed Buffers: Min: 0, Avg: 1.2, Max: 4, Diff: 4, Sum: 10]
[Scan RS (ms): Min: 0.0, Avg: 0.0, Max: 0.2, Diff: 0.2, Sum: 0.3]
[Code Root Scanning (ms): Min: 0.0, Avg: 0.7, Max: 3.4, Diff: 3.4, Sum: 5.4]
[Object Copy (ms): Min: 0.7, Avg: 7.7, Max: 9.6, Diff: 8.8, Sum: 61.5]
[Termination (ms): Min: 0.0, Avg: 0.0, Max: 0.0, Diff: 0.0, Sum: 0.1]
[Termination Attempts: Min: 1, Avg: 9.6, Max: 16, Diff: 15, Sum: 77]
[GC Worker Other (ms): Min: 0.0, Avg: 0.1, Max: 0.3, Diff: 0.2, Sum: 0.7]
[GC Worker Total (ms): Min: 12.1, Avg: 13.1, Max: 14.0, Diff: 1.9, Sum: 104.7]
[GC Worker End (ms): Min: 5979.6, Avg: 5979.6, Max: 5979.8, Diff: 0.2]
[Code Root Fixup: 0.3 ms]
[Code Root Purge: 0.0 ms]
[Clear CT: 1.2 ms]
[Other: 3.4 ms]
[Choose CSet: 0.0 ms]
[Ref Proc: 1.6 ms]
[Ref Enq: 0.0 ms]
[Redirty Cards: 1.4 ms]
[Humongous Register: 0.0 ms]
[Humongous Reclaim: 0.0 ms]
[Free CSet: 0.1 ms]
[Eden: 31.0M(68.0M)->0.0B(69.0M) Survivors: 8192.0K->7168.0K Heap: 40.4M(128.0M)->9115.0K(128.0M)]
[Times: user=0.10 sys=0.01, real=0.02 secs]

上面的日志表明发生了YGC、应用线程为此暂停了20毫秒,Eden区被清空。日志里面initial-mark的字样表明后台的并发GC阶段开始了。因为初始标记阶段本身也是要暂停应用线程的,G1正好在YGC的过程中把这个事情也一起干了。

  1. 根区域扫描:G1扫描Survivor区直接可达的老年代区域对象,并标记被引用的对象(必须在Young GC之前完成),这个过程没有暂停应用线程;是后台线程并行处理的。这个阶段不能被YGC所打断。如果Young区空间恰好在Root扫描的时候满了、YGC必须等待root扫描之后才能进行。带来的影响是YGC暂停时间会相应的增加。
  • 不能中断原因

不论是young GC还是mixed GC,都需要回收young region。因为实际上RSet是不记录从young region出发的指针,那么就可能出现一种情况,一个老年代的存活对象,只被年轻代的对象引用。在一次young GC中,这些存活的年轻代的对象会被复制到Survivor Region,因此需要扫描这些Survivor region来查找这些指向老年代的对象的引用,作为并发标记阶段扫描老年代的根的一部分,所以扫描跟区域时Survivor 区域不能改变也就是不能触发young GC。

  • 日志
2021-09-02T14:37:51.706+0800: 5.986: [GC concurrent-root-region-scan-start]
2021-09-02T14:37:51.719+0800: 5.999: [GC concurrent-root-region-scan-end, 0.0130445 secs]
//这是扫描时刚好发生了YGC,这时YGC暂停,
//YGC暂停这里可以看出在root扫描结束之前就发生了,
//表明YGC发生了等待,等待时间大概是100毫秒。
350.994: [GC pause (young)
351.093: [GC concurrent-root-region-scan-end, 0.6100090]
351.093: [GC concurrent-mark-start],0.37559600 secs]
  1. 并发标记:在整个堆中进行并发标记(与程序线程并发执行),此过程可能会被Young GC打断,在并发标记阶段中,若发现某些region中所有对象都是垃圾,那这个region就会被立即回收,同时并发标记过程中,会计算每个region的对象活性(该region存活对象的比例,G1垃圾回收的时候并不是所有region都会参与回收的,根据回收的价值高低来优先回收价值较高的region)
  • 日志
//并发标记没有被YGC打断
2021-09-02T14:37:59.294+0800: 13.574: [GC concurrent-mark-start]
2021-09-02T14:37:59.346+0800: 13.625: [GC concurrent-mark-end, 0.0513088 secs]
//并发标记被YGC打断
2021-09-02T10:28:10.473+0800: 0.243: [GC concurrent-mark-start]
2021-09-02T10:28:10.473+0800: 0.243: [GC pause (G1 Humongous Allocation) (young), 0.0011077 secs]
[Parallel Time: 0.8 ms, GC Workers: 8]
[GC Worker Start (ms): Min: 243.0, Avg: 243.0, Max: 243.2, Diff: 0.3]
[Ext Root Scanning (ms): Min: 0.0, Avg: 0.1, Max: 0.2, Diff: 0.2, Sum: 1.1]
[Update RS (ms): Min: 0.0, Avg: 0.0, Max: 0.0, Diff: 0.0, Sum: 0.0]
[Processed Buffers: Min: 0, Avg: 0.0, Max: 0, Diff: 0, Sum: 0]
[Scan RS (ms): Min: 0.0, Avg: 0.0, Max: 0.0, Diff: 0.0, Sum: 0.0]
[Code Root Scanning (ms): Min: 0.0, Avg: 0.0, Max: 0.0, Diff: 0.0, Sum: 0.0]
[Object Copy (ms): Min: 0.4, Avg: 0.4, Max: 0.5, Diff: 0.1, Sum: 3.5]
[Termination (ms): Min: 0.0, Avg: 0.0, Max: 0.0, Diff: 0.0, Sum: 0.3]
[Termination Attempts: Min: 1, Avg: 5.1, Max: 9, Diff: 8, Sum: 41]
[GC Worker Other (ms): Min: 0.0, Avg: 0.0, Max: 0.0, Diff: 0.0, Sum: 0.1]
[GC Worker Total (ms): Min: 0.4, Avg: 0.6, Max: 0.7, Diff: 0.3, Sum: 5.0]
[GC Worker End (ms): Min: 243.7, Avg: 243.7, Max: 243.7, Diff: 0.0]
[Code Root Fixup: 0.0 ms]
[Code Root Purge: 0.0 ms]
[Clear CT: 0.1 ms]
[Other: 0.2 ms]
[Choose CSet: 0.0 ms]
[Ref Proc: 0.1 ms]
[Ref Enq: 0.0 ms]
[Redirty Cards: 0.1 ms]
[Humongous Register: 0.0 ms]
[Humongous Reclaim: 0.0 ms]
[Free CSet: 0.0 ms]
[Eden: 1024.0K(3072.0K)->0.0B(1024.0K) Survivors: 1024.0K->1024.0K Heap: 3967.2K(10.0M)->3057.4K(10.0M)]
[Times: user=0.00 sys=0.00, real=0.00 secs]
2021-09-02T10:28:10.475+0800: 0.244: [GC concurrent-mark-end, 0.0013878 secs]
  1. 重新标记(Stop-The-World)。由于并发标记阶段是收集器的标记线程和程序线程并发执行的,需要进行再次标记,修正上一次的标记结果,可以理解为增量补偿标记。会出现STW(暂停时间较短
  • 日志
2021-09-02T14:37:51.729+0800: 6.008: [GC remark 2021-09-02T14:37:51.729+0800: 6.008: [Finalize Marking, 0.0054074 secs] 2021-09-02T14:37:51.735+0800: 6.014: [GC ref-proc, 0.0018650 secs] 2021-09-02T14:37:51.736+0800: 6.016: [Unloading, 0.0092689 secs], 0.0178767 secs]
  1. 独占清理:不是百分之百为垃圾的内存分段并不会被处理,这些内存分段中的垃圾是在混合回收过程(Mixed GC)中被回收的。由于Humongous对象会独占整个内存分段,如果Humongous对象变为垃圾,则内存分段百分百为垃圾,所以会在第一时间被回收掉。
  • 日志
2021-09-02T10:28:10.476+0800: 0.246: [GC cleanup 4122K->4122K(10M), 0.0004081 secs]
[Times: user=0.00 sys=0.00, real=0.00 secs]

混合回收(Mixed GC)

  • 包括年轻代和老年代的垃圾回收
  • 标记完成后马上开始垃圾的回收。对于一个混合的回收过程,G1从老年代移动存活的对象到空闲区域,这些空闲的区域变成了老年代region。当越来越多的对象晋升到老年代region的时候,为了避免堆内存被耗尽,就会触发一个混合垃圾收集Mixed GC,该算法并不是一个Old GC也不是Full GC,除了回收整个Young region之外,还会回收一部分Old region.
  • 在并发标记结束之后,老年代中能够完全确认为垃圾的region中的内存分段被回收了,部分为垃圾的region也被计算出来了,默认情况下,这些老年代的region会被分为8次回收(可以通过-XX:G1MixedGCCountTarget设置)。
  • 混合回收的回收集包括1/8的老年代的内存分段,Eden区,Survivor区,混合回收的算法和年轻代回收的算法完全一致
    • 混合回收并不一定要进行8次,有一个阈值设置:-XX:G1HeapWastePercent,默认值5%,代表允许整个堆内存中有5%的内存可以被浪费,意味着如果发现空闲出来的Region数量达到了堆内存5%,则不进行混合回收,因为GC花费的时间相对于较少的垃圾回收来说得不偿失。
  • 由于老年代的内存分段默认分为8次回收,G1会优先回收垃圾多的内存分段,垃圾占内存分段比例越高的会优先被回收。并且有一个阈值决定内存分段是否被回收:-XX:G1MixedGCLiveThresholdPercent,默认为85%,代表存活对象占内存分段比例要低于85%来回被回收,如果垃圾占比太低,意味着存活的对象多,复制算法就会花费更多的时间区复制存活的对象。
  • 日志
2021-09-02T14:39:04.407+0800: 78.687: [GC pause (G1 Evacuation Pause) (mixed)
Desired survivor size 524288 bytes, new threshold 1 (max 15)
- age   1:     648264 bytes,     648264 total
- age   2:     335208 bytes,     983472 total
- age   3:    1301088 bytes,    2284560 total
- age   4:    1518296 bytes,    3802856 total
, 0.0353900 secs]
[Parallel Time: 29.0 ms, GC Workers: 8]
[GC Worker Start (ms): Min: 78687.2, Avg: 78689.5, Max: 78701.1, Diff: 14.0]
[Ext Root Scanning (ms): Min: 0.0, Avg: 1.7, Max: 5.8, Diff: 5.7, Sum: 13.9]
[Update RS (ms): Min: 0.0, Avg: 3.6, Max: 5.2, Diff: 5.2, Sum: 28.7]
[Processed Buffers: Min: 0, Avg: 2.8, Max: 6, Diff: 6, Sum: 22]
[Scan RS (ms): Min: 0.0, Avg: 5.1, Max: 6.2, Diff: 6.2, Sum: 40.5]
[Code Root Scanning (ms): Min: 0.0, Avg: 0.3, Max: 0.9, Diff: 0.9, Sum: 2.1]
[Object Copy (ms): Min: 13.6, Avg: 14.6, Max: 15.2, Diff: 1.7, Sum: 116.9]
[Termination (ms): Min: 0.0, Avg: 0.3, Max: 0.5, Diff: 0.5, Sum: 2.7]
[Termination Attempts: Min: 1, Avg: 2.8, Max: 5, Diff: 4, Sum: 22]
[GC Worker Other (ms): Min: 0.0, Avg: 0.0, Max: 0.0, Diff: 0.0, Sum: 0.3]
[GC Worker Total (ms): Min: 14.0, Avg: 25.6, Max: 27.9, Diff: 13.9, Sum: 204.9]
[GC Worker End (ms): Min: 78715.1, Avg: 78715.1, Max: 78715.2, Diff: 0.1]
[Code Root Fixup: 0.6 ms]
[Code Root Purge: 0.0 ms]
[Clear CT: 1.2 ms]
[Other: 4.7 ms]
[Choose CSet: 0.1 ms]
[Ref Proc: 2.8 ms]
[Ref Enq: 0.0 ms]
[Redirty Cards: 1.2 ms]
[Humongous Register: 0.0 ms]
[Humongous Reclaim: 0.0 ms]
[Free CSet: 0.2 ms]
[Eden: 1024.0K(1024.0K)->0.0B(5120.0K) Survivors: 7168.0K->1024.0K Heap: 67.6M(128.0M)->58.1M(128.0M)]
[Times: user=0.23 sys=0.01, real=0.04 secs]

FULL GC

  • 必要的情况下(对象分配速度远大于回收速度),Full GC仍然会触发(Full GC的成本较高,单线程,性能差,STW时间长)
  • 堆内存太小、对象分配速度远大于回收速度等原因都可以导致G1在复制存活对象的时候没有空闲的内存分段可用,最终造成Full GC的触发
2021-09-02T10:48:40.887+0800: 0.314: [Full GC (Allocation Failure)  3887K->3680K(10M), 0.0070231 secs]
[Eden: 0.0B(1024.0K)->0.0B(1024.0K) Survivors: 1024.0K->0.0B Heap: 3887.3K(10.0M)->3680.6K(10.0M)], [Metaspace: 3209K->3209K(1056768K)]
[Times: user=0.02 sys=0.00, real=0.01 secs]

G1比CMS的优势

  1. 压缩空间的优势: CMS-标记清除算法,内存碎片多。 G1用的是拷贝算法,直接清除原空间Region分区,避免内存碎片问题
  2. Eden,Survivor,Old区不固定。是因为,G1没有显示固定设置每个代的空间大小。只是要求每个分区的大小是一样的就行。哪个区域不够开辟哪个空间
  3. G1可以通过设置预测模型,设置运行时间:指定的预判收集时间,显示的控制用户指定的收集时间
  4. G1采用的是内存拷贝的方法,直接清空释放空间。
  5. G1能够在年轻代使用。CMS只使用与老年代。
  6. 有了G1以后,是不是还有一些场景采用“ParNew+CMS”垃圾回收器也可以呢?g1适合 大堆的场景 或者有业务不能有太高的延时 如果业务上不需要特别大的堆 或者 业务属于不需要及时反馈用户的 比如贷款业务 申请额度之后就后台处理了 有额度以后 在通知你这个时候 par new 加 cms可以用的

案例1

假设一个前提,这个纯碎就是我们人为设定的,就是假设在这个系统里,G1回收掉300个Region(600MB内存),大致需要200ms。那么很有可能系统运行时,G1呈现出如下的运行效果。
首先,随着系统运行,每秒创建3MB的对象,大概1分钟左右就会塞满100个Region(200MB内存)。此时很可能G1会觉得,要是我现在就触发一次新生代gc,那么回收区区200MB只需要大概几十ms,最多就让系统停顿几十ms而已,跟我的主人设定的-XX:MaxGCPauseMills参数限制的200ms停顿时间相差甚远。要是我现在就触发新生代gc,那岂不是会导致回收完过后接着1分钟再次让新生代这100个Region塞满,接着又触发新生代gc?那这样算下来,岂不是每分钟都要执行一次新生代gc?是不是太频繁了?好像没这个必要吧!所以还不如给新生代先增加一些Region,然后让系统继续运行着在新生代Region中分配对象好了,这样就不用过于频繁的触发新生代gc了.
然后系统继续运行,一直到可能300个Region都占满了,此时通过计算发现回收这300个Region大概需要200ms,那么可能这个时候就会触发一次新生代gc了。

说明

上述所有的gc日志案例的配置为

 -XX:+HeapDumpAfterFullGC
-XX:+HeapDumpOnOutOfMemoryError
-XX:HeapDumpPath=/home/gc
-XX:InitialHeapSize=134217728
-XX:MaxHeapSize=134217728
-XX:PretenureSizeThreshold=1048576
-XX:+PrintGC
-XX:+PrintGCApplicationConcurrentTime
-XX:+PrintGCApplicationStoppedTime
-XX:+PrintGCDateStamps
-XX:+PrintGCDetails
-XX:+PrintGCTimeStamps
-XX:+PrintTenuringDistribution
-XX:SurvivorRatio=8
-XX:+UseCompressedClassPointers
-XX:+UseCompressedOops
-XX:+UseG1GC

正文完
 0
yangleduo
版权声明:本站原创文章,由 yangleduo 于2023-04-07发表,共计13164字。
转载说明:除特殊说明外本站文章皆由CC-4.0协议发布,转载请注明出处。