G1垃圾回收器

news2025/1/11 14:12:19

概述

  • 官网

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中。

在这里插入图片描述

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工作原理

每次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 

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/25026.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

高项 进度管理论文

七个过程: 项目进度管理包括为管理项目按时完成所需的7个过程,具体为: (1)规划进度管理:为规划、编制、管理、执行和控制项目进度而制定政策、程序和文档过程。 (2)定义活动&…

【C语言】初阶指针的简单使用 _指针运算 指针和数组的关系[初阶篇 _学习专用]

1.指针是什么? 在学习指针的时候,我们经常会看到下面这段代码: int main() {int a 10;int* pa &a;*pa 20; } 之前并没有接触过指针的朋友们看到后可能是一头雾水,根本不知道从哪里去理解;下面我们就通过一些…

STM32开发时HardFault错误的排查

STM32开发时HardFault错误的排查 本篇是 嵌入式开发-STM32硬件I2C驱动OLED屏 一文的扩展。 把相关的问题记录一下,给遇到HardFault_Handler问题的朋友做个参考。 故障现象 做STM32开发,经常遇到HardFault错误,也就是程序不会正常运行&…

WPF动画(2)

动画的生命周期 从技术的角度看,WPF动画只是暂时的,这意味着它们不能真正改变基本属性的 值,当动画处于活动状态时,只是覆盖了属性的值 。 单向动画,在动画运行结束后会保持处于活动状态,这是因为动画需要…

(十一)手写简单的Spring框架

文章目录第一步:搭建环境第二步:准备好要管理的Bean第三步:准备myspring.xml配置文件第四步:编写MyApplicationContext接口第五步:编写MyClassPathXmlApplicationContext第六步:采用Map集合存储Bean第七步&…

第十一章 Golang面向对象编程(下)

面向对象编程三大特性 基本介绍 Golang仍然有面向对象编程的继承,封装和多态的特性,只是实现的方式和其他OOP语言不一样。 封装 面向对象编程思想-抽象 我们在前面去定义一个结构体的时候,实际上就是把一类事物共有的属性(字段…

Presto 之 explain and explain analyze的实现

一. 前言 本文主要探索在Presto中Explain功能是如何实现的。在Presto中,Explain用法有两种,一种是单纯的explain,此场景只会显示经过RBO优化后的执行计划,但是查询并不会真正地执行。第二种是explain analyze,此场景会…

JVM常用参数

JVM内存相关的几个核心参数 -Xms:Java堆内存初始大小-Xmx:Java堆内存的最大大小-Xmn:Java堆内存中的新生代大小,扣除新生代剩下的就是老年代的内存大小了-XX:PermSize:永久代大小-XX:MaxPermSize:永久代最…

疾控物资管理系统-疾控中心物资管理系统

一、系统概述 东识科技(DONWIT)疾控中心物资管理系统(智物资DW-S300)是依托互3D技术、云计算、大数据、RFID技术、数据库技术、AI、视频分析技术对RFID智能仓库进行统一管理、分析的信息化、智能化、规范化的系统。 随着疫情的突…

(STM32)从零开始的RT-Thread之旅--GPIO

上一篇: (STM32)从零开始的RT-Thread之旅--基础项目构建与时钟配置 无论什么开发板,最先调试的肯定是GPIO,一般用来用作指示灯或者按键输入。本篇只是很简单的GPIO应用,没有具体分析RTT框架实现。 首先先创建一个BSP文件夹&…

机器人操作系统ROS(21) jetson nano安装torch tensorflow

安装torch、tensorflow其实跟普通在Linux系统安装没有区别,但是Linux是arch64位的,而jetson是aarch64位的,所以还是不太一样。 另外一个坑是:购买的创乐博的机器人,已经安装ros,但是安装torh的时候需要apt …

使用Spring实现工厂+策略模式

使用Spring实现工厂策略模式 这里使用发短信业务&#xff1a; 不同短信有不同模板但是发送方法都相同只是发送内同不同 1. 定义短信发送策略接口&#xff1a; //策略接口 public interface SmsTemStrategy {public void sendSms(Map<String,String> params); }2.短信…

【Python】Numpy傅里叶变换总结

文章目录简介fft简介 Fourier变换极其逆变换在数学上的定义如下 F(ω)∫−∞∞f(t)e−iωtdtf(t)π2∫−∞∞F(ω)eiωtdωF(\omega)\int^\infty_{-\infty}f(t)e^{-i\omega t}\text dt\\ f(t)\frac{\pi}{2}\int^\infty_{-\infty}F(\omega)e^{i\omega t}\text d\omega F(ω)∫−…

飞行机器人专栏(八)-- AGX Xavier 通信、控制及视觉应用开发

目录 0. Introduction of Jetson Developer kits 1. 硬件对比 Jetson 模组系列 Jetson AGX Orin 系列 Jetson Orin NX 系列 Jetson AGX Xavier 系列 Jetson Xavier NX 系列 Jetson TX2 系列 Jetson Nano 2. 应用场景 1. Introduction of AGX Xavier Taking Perform…

HTML+PHP+MySQL实现新闻列表模块(1+X Web前端开发中级 例题)——初稿

&#x1f4c4;题目要求 阅读下列说明、效果图、MySQL数据库操作和代码&#xff0c;进行动态网页开发&#xff0c;填写&#xff08;1&#xff09;至&#xff08;15&#xff09;代码。&#x1f9e9;说明 该程序为一个html和php混合的新闻列表模块&#xff0c;使用PHP语言&#x…

【深入浅出Spring6】第九期——Spring对事务的支持

因为很多系统都包含事务&#xff0c;所以Spring提供了相关的Api&#xff0c;属于AOP切面编程的二次封装 那么什么是事务&#xff08;Transaction&#xff09;呢&#xff1f; 指访问并可能更新数据库中各种数据项的一个程序执行单元(unit)在一个业务流程中可能需要多条DML联合执…

【Hack The Box】Linux练习-- OpenAdmin

HTB 学习笔记 【Hack The Box】Linux练习-- OpenAdmin &#x1f525;系列专栏&#xff1a;Hack The Box &#x1f389;欢迎关注&#x1f50e;点赞&#x1f44d;收藏⭐️留言&#x1f4dd; &#x1f4c6;首发时间&#xff1a;&#x1f334;2022年11月22日&#x1f334; &#x…

Flutter组件--OverflowBox、SizedOverflowBox(子组件超出父组件裁剪)

1.OverflowBox介绍 OverflowBox 允许子控件超出父控件的边界。这个特性主要可以用来实现文字或者按钮角标的. OverflowBox构造函数 const OverflowBox({Key? key,this.alignment Alignment.center,this.minWidth,this.maxWidth,this.minHeight,this.maxHeight,Widget? ch…

java面试强基(8)

String、StringBuffer、StringBuilder 的区别&#xff1f; 可变性 ​ String 是不可变的。StringBuilder 与 StringBuffer 都继承自 AbstractStringBuilder 类&#xff0c;在 AbstractStringBuilder 中也是使用字符数组保存字符串&#xff0c;不过没有使用 final 和 private …

Air780E涂鸦云远程开关-LuatOS

Air780E涂鸦云远程开关-LuatOS 涂鸦智能在远程开关和灯控领域可以算是龙头了&#xff0c;今天就来学习一下&#xff0c;如何接入涂鸦云平台 一、涂鸦云准备 注册账号不写了&#xff0c;自己注册账号即可 1、创建产品 点击产品->极速智能化->产品开发页面的创建产品 …