jvm · 2022-07-31 0

jvm 垃圾回收器

一、垃圾收集器发展史

有了虚拟机,就一定需要收集垃圾的机制,这就是Garbage Collection,对应的产品我们称为Garbage Collector。

  1. 1999年随JDK1.3.1一起来的是串行方式的Serial GC,它是第一款GC。ParNew垃圾收集器是Serial收集器的多线程版本
  2. 2002年2月26日,Parallel GC和Concurrent Mark Sweep GC跟随JDK1.4.2一起发布
  3. Parallel GC在JDK6之后成为HotSpot默认GC
  4. 2012年,在JDK1.7u4版本中,G1可用
  5. 2017年,JDK9中G1变成默认的垃圾收集器,以替代CMS
  6. 2018年3月,JDK10中G1垃圾回收器的并行完整垃圾回收,实现并行性来改善最坏情况下的延迟
  7. 2018年9月,JDK11发布。引入Epsilon 垃圾回收器,又被称为 “No-Op(无操作)“ 回收器。同时,引入ZGC:可伸缩的低延迟垃圾回收器(Experimental)
  8. 2019年3月,JDK12发布。增强G1,自动返回未用堆内存给操作系统。同时,引入Shenandoah GC:低停顿时间的GC(Experimental)
  9. 2019年9月,JDK13发布。增强ZGC,自动返回未用堆内存给操作系统
  10. 2020年3月,JDK14发布。删除CMS垃圾回收器。扩展ZGC在macOS和Windows上的应用

二、垃圾收集器类型

回收器类型:

  1. 串行回收器: Serial 、Serial Old
  2. 并行回收器: ParNew、Parallel Scavenge、 Parallel Old
  3. 并发回收器: CMS、 G1

回收区域:

  1. 新生代 Serial GC 、 Parallel Scavenge 、 ParNew GC
  2. 老年代 Serial Old GC 、 Parallel Old GC、 CMS
  3. 整堆收集器 G1

并行说的是GC 线程之间的关系,而并发说的是GC和用户线程之间的关系

  • 并行:同一时间有多条这样的线程在协同工作,但是此时用户线程时等待状态
  • 并发:同一时间GC和用户线程可以一起工作一起运行。因此程序依然能够响应用户线程的操作但是由于GC线程也占用了一部分系统资源,所以此时的用户线程处理的效率会下降
回收器 类型 算法 作用位置 特点 使用场景
SerialGC 串行收集器 复制算法 新生代 响应速度快 适合单核的客户端应用程序下
SerialGCOld 串行收集器 标记整理算法 老年代 响应速度快 适合单核的客户端应用程序下
ParNew 并行收集器 复制算法 新生代 响应速度快 适合多核的cpu情况下,默认与cms配合使用
Parallel 并行收集器 复制算法 新生代 吞吐量优先 适合于后端多核cpu情况下 堆内存不是很大
Parallel old 并行收集器 标记整理算法 老年代 老年代吞吐量优先 适合于后端多核cpu情况下 堆内存不是很大
CMS 收集器 并发收集器 标记清除 老年代 响应速度快 适合于企业级B/S项目
G1收集器 并发收集器 标记整理算法/复制算法 老年代/新生代 响应速度优先 适合于大型服务器端

GC类型主要分为:

  • MinorGC/YoungGC:只是新生代(Eden,S0,S1)的垃圾收集
  • MajorGC/OldGC:只是老年代的垃圾收集。
  • FullGC:收集整个java堆(新生代、老年代)和方法区(永久代)的垃圾收集。

注意:只有CMS收集器会有单独收集老年代的行为,其他收集器均无此行为,老年代空间不足会直接触发Full GC。而针对新生代的MinorGC,各个收集器均支持。总之,单独发生收集行为的只有新生代,除了CMS收集器,都不支持单独回收老年代。这也是为什么我们经常提到的只有Young GC和Full GC,而很少提到Old GC的原因。

频繁推出垃圾回收器的原因?主要解决STW问题。

MajorGC 的速度一般会比 Minor GC 慢 10 倍以上。

三、JVM 相关参数

1.栈内存大小相关设置

-Xss1024k -XX:ThreadStackSize=1024k 设置线程栈占用内存大小

根据官方文档, 它设置的是一个线程 Stack 的大小. 若是不设置, 根据操作系统的不同, 有不同的默认值. 如 64位的 Linux 下, 默认是1MB

2.堆内存大小相关设置

-Xms512m 设置堆内存初始值大小。默认值:如果未设置,初始值将是老年代和年轻代分配制内存之和
-Xmx1024m -XX:MaxHeapSize=1024m 设置堆内存最大值

3.年轻代内存大小相关设置

-Xmn512m 设置新生代的初始值及最大值。默认值:堆内存的1/4(这里要记住不是最大堆内存,还是已经分配的堆内存的1/4)
-XX:NewSize=512m 设置新生代的初始值
-XX:MaxNewSize=512m设置新生代的最大值

4.比率方式设置

-XX:NewRatio=2 设置年轻代和老年代的比例。表示年轻代内存:老年代内存=1:2
-XX:SurvivorRatio=8 两个Survivor区和Eden区的堆空间比值为 8,表示 S0:S1:Eden = 1:1:8
-XX:MinHeapFreeRatio=40 最小堆使用比例,堆内存剩余空间低于这个比例时会开始变大
-XX:MaxHeapFreeRatio=70 最大堆可用比例,当堆内存剩余空间高于这个比例时,会开始变小

5.Meta大小相关设置

-XX:MetaspaceSize=128m 初始元空间大小,达到该值就会触发垃圾收集进行类型卸载,同时GC会对该值进行调整:如果释放了大量的空间,就适当降低该值;如果释放了很少的空间,那么在不超过MaxMetaspaceSize时,适当提高该值
-XX:MaxMetaspaceSize=256m 设置元空间的最大值,默认是没有上限的,也就是说你的系统内存上限是多少它就是多少

我们希望的是一启动就分配个最大内存,避免它运行中扩容影响服务;所以一般 JAVA 程序还会将 Xms和Xmx配置为相等的大小,避免这个扩容的操作。

四、内存分配与回收策略

1.内存分配

堆是整个内存模型中占用最大的一部分,而且不是连续的。当有需要分配内存的时候,一般有两个方法分配,指针碰撞和空闲列表。

2.回收策略

堆的内存回收是由虚拟机的垃圾收集器GC进行管理的。

Minor GC:Eden区满或者快满的时候触发GC。复制法。
FULL GC:当年老代内存不足的话就会触发垃圾收集。默认是占用了68%后收集,可用参数 -XX:CMSInitiatingOccupancyFraction=68自行设置。年老和永久区垃圾收集的方法都是“标记-清除-整理”。标记好了就清除掉,最后整理成逻辑连续的区域。

标记,一般有两种方法:引用计数和可达性分析。

引用计数:就是给对象添加一个引用计数器,每当有一个地方引用它时就加1,引用失效时就减1,当计数器为0的时候就标记为可回收。这种判断效率很高,但是很多主流的虚拟机并没有采用这种方法,主要是因为它很难解决几个对象之间循环引用的问题

可达性分析:通过将一些称为"GC Roots"的对象作为起始点,从这些节点开始搜索,搜索和该节点发生直接或者间接引用关系的对象,将这些对象以链的形式组合起来,形成一张“关系网”,又叫做引用链。最后垃圾收集器就回收那些不在这张关系网上的对象。目前主流的商用虚拟机用的都是类似的方法。那什么对象才能作为“GC Roots”呢?在java中,有四种对象可以作为“GC Roots”。

  1. 栈帧中的引用对象。(栈中的)
  2. 静态属性引用的对象。(方法区中的)
  3. 常量引用的对象。(方法区中的)
  4. 本地方法栈中JNI引用的对象。(本地方法栈中的)

垃圾回收并不会阻塞我们程序的线程,他是与当前程序并发执行的。所以问题就出在这里,当GC线程标记好了一个对象的时候,此时我们程序的线程又将该对象重新加入了“关系网”中,当执行二次标记的时候,该对象也没有重写finalize()方法,因此回收的时候就会回收这个不该回收的对象。

虚拟机的解决方法就是在一些特定指令位置设置一些“安全点”,当程序运行到这些“安全点”的时候就会暂停所有当前运行的线程(Stop The World 所以叫STW),暂停后再找到“GC Roots”进行关系的组建,进而执行标记和清除。

这些特定的指令位置主要在:

  1. 循环的末尾;
  2. 方法临返回前 / 调用方法的call指令后;
  3. 可能抛异常的位置;

Full GC时STW的时间相对GC来说时间很长,因为Full GC针对整个堆以及永久代的,因此整个GC的范围大大增加;还有就是JVM回收算法就是我们之前说过的“标记–清除–整理”,这里也会损耗一定的时间。所以在优化JVM的时候,减少Full GC的次数也是经常用到的办法。

查看GC 日志 -XX:+PrintGCDetails -Xloggc:/home/zxm/gc.log

五、垃圾回收过程

清理新生代(Eden区和Survivor区)叫 Minor GC;清理年老代(Old区)叫 Major GC;清理整个堆空间(年轻代和老年代)以及 元空间(方法区)叫Full GC。

MetaspaceSize触发FullGC的阈值,默认约为21M,如做了配置,最小阈值为自定义配置大小。空间使用达到阈值,触发FullGC,同时对该值扩大。当然如果元空间实际使用小于阈值,在GC的时候也会对该值缩小

MaxMetaspaceSize为元空间的最大值,如果设置太小,就会和上面提到的一样,可能会导致频繁FullGC,甚至OOM

1.新生代垃圾回收

  1. 新new的对象都放在Eden区
  2. 新生代的GC算法是 复制算法 。Eden区满或者快满的时候触发GC时(Minor Gc);在GC刚开始的时候,对象只会存在于Eden区和名为 From 的Survivor区,Survivor区 To 是空的。紧接着进行GC,Eden区中所有存活的对象都会被复制到 To 区,而在 From 区中,仍存活的对象会根据他们的年龄值来决定去向
  3. 年龄达到一定值(年龄阈值,可以通过 -XX:MaxTenuringThreshold 来设置,默认是 15 )的对象会被移动到年老代中,没有达到阈值的对象会被复制到Survivor To 区域
  4. 经过这次GC后,Eden区和From区已经被清空。这个时候, From和To 会交换它们的角色,也就是新的 To 就是上次GC前的 From,新的 From 就是上次GC前的 To
  5. 不管怎样,都会保证名为 To 的Survivor区域是空的。Minor GC会一直重复这样的过程,直到 To 区被填满, To 区被填满之后,会将所有对象移动到年老代中
  6. 注意:当在触发Minor GC时,发现堆新生代中还存活的年龄达到阀值的对象的容量,比目前年老代中剩余空间还大的话,说明年老代存放不下,就会直接触发一次Full GC,而不再去触发Minor GC 了

新生代对象晋升到年老代的条件:

  1. 如果对象的大小大于Eden的二分之一会直接分配在old年老代,如果old也分配不下,会做一次major GC,如果小于Eden的一半但是没有足够的空间,就进行minor GC也就是新生代GC
  2. minor GC后,Survivor仍然放不下,则放到Old老年代
  3. 动态年龄判断 ,大于等于某个年龄的对象超过了Survivor空间一半 ,大于等于某个年龄的对象直接进入老年代

2.年老代垃圾回收

Full GC触发条件:

  1. 手动调用System.gc,会不断的执行Full GC
  2. 老年代空间不足/满了
  3. 方法区空间不足/满了

stop-the-world,它会在任何一种GC算法中发生。stop-the-world 意味着JVM因为需要执行GC而停止应用程序的执行

当stop-the-world 发生时,除GC所需的线程外,所有的线程都进入等待状态,直到GC任务完成。GC优化很多时候就是减少stop-the-world 的发生。

JVM GC只回收堆内存和方法区内的对象。而栈内存的数据,在超出作用域后会被JVM自动释放掉,所以其不在JVM GC的管理范围内。

六、设置垃圾回收器

JVM内存占用=操作系统自身耗内存 + 堆 + Java永久代/元数据区/方法区/常量池/代码缓存 + 程序计数器(可忽略不计)*线程数 + 虚拟机进程本身 + 虚拟机栈(线程栈)*线程数 + 本地方法栈(JNI调用)*线程数 + 直接内存(Java NIO)

1. -XX:+UseSerialGC

新生代和老年代都使用串行收集器
串行收集器使用单线程并且是独占式的垃圾回收

例:jdk11 -Xmx2G -Xms2G -Xmn1G -XX:MetaspaceSize=128M -XX:MaxMetaspaceSize=128M -XX:+UseSerialGC

当有具体大小定义时(-Xmn),那么-XX:NewRatio比例设置将失效
由SurvivorRatio=8,则from:to:eden=1:1:8

jvm 参数:

zxm@zxm-pc:~$ jinfo -flags 28229
VM Flags:
-XX:-BytecodeVerificationLocal -XX:-BytecodeVerificationRemote -XX:CICompilerCount=4 -XX:CompressedClassSpaceSize=125829120 -XX:InitialHeapSize=2147483648 -XX:+ManagementServer -XX:MaxHeapSize=2147483648 -XX:MaxMetaspaceSize=134217728 -XX:MaxNewSize=1073741824 -XX:MetaspaceSize=134217728 -XX:MinHeapDeltaBytes=196608 -XX:NewSize=1073741824 -XX:NonNMethodCodeHeapSize=7549744 -XX:NonProfiledCodeHeapSize=244108496 -XX:OldSize=1073741824 -XX:ProfiledCodeHeapSize=0 -XX:ReservedCodeCacheSize=251658240 -XX:+SegmentedCodeCache -XX:TieredStopAtLevel=1 -XX:+UseCompressedClassPointers -XX:+UseCompressedOops -XX:+UseSerialGC 

jvm 堆:

zxm@zxm-pc:~$ jhsdb jmap --heap --pid 28229
Attaching to process ID 28229, please wait...
Debugger attached successfully.
Server compiler detected.
JVM version is 11.0.9+7-LTS

using thread-local object allocation.
Mark Sweep Compact GC

Heap Configuration:
   MinHeapFreeRatio         = 40
   MaxHeapFreeRatio         = 70
   MaxHeapSize              = 2147483648 (2048.0MB)
   NewSize                  = 1073741824 (1024.0MB)
   MaxNewSize               = 1073741824 (1024.0MB)
   OldSize                  = 1073741824 (1024.0MB)
   NewRatio                 = 2
   SurvivorRatio            = 8
   MetaspaceSize            = 134217728 (128.0MB)
   CompressedClassSpaceSize = 125829120 (120.0MB)
   MaxMetaspaceSize         = 134217728 (128.0MB)
   G1HeapRegionSize         = 0 (0.0MB)

Heap Usage:
New Generation (Eden + 1 Survivor Space):
   capacity = 966393856 (921.625MB)
   used     = 607083512 (578.9599533081055MB)
   free     = 359310344 (342.66504669189453MB)
   62.81947140193739% used
Eden Space:
   capacity = 859045888 (819.25MB)
   used     = 606960320 (578.8424682617188MB)
   free     = 252085568 (240.40753173828125MB)
   70.65516853972765% used
From Space:
   capacity = 107347968 (102.375MB)
   used     = 123192 (0.11748504638671875MB)
   free     = 107224776 (102.25751495361328MB)
   0.11475950807005494% used
To Space:
   capacity = 107347968 (102.375MB)
   used     = 0 (0.0MB)
   free     = 107347968 (102.375MB)
   0.0% used
tenured generation:
   capacity = 1073741824 (1024.0MB)
   used     = 17019696 (16.231246948242188MB)
   free     = 1056722128 (1007.7687530517578MB)
   1.5850827097892761% used

查看gc:

zxm@zxm-pc:~$ jstat -gc 28229
 S0C    S1C    S0U    S1U      EC       EU        OC         OU       MC     MU    CCSC   CCSU   YGC     YGCT    FGC    FGCT    CGC    CGCT     GCT   
104832.0 104832.0  0.0   120.3  838912.0 592750.3 1048576.0   16620.8   38656.0 36968.4 5120.0 4462.2     31    0.410   0      0.000   -          -    0.410
zxm@zxm-pc:~$ jstat -gcutil 28229
  S0     S1     E      O      M     CCS    YGC     YGCT    FGC    FGCT    CGC    CGCT     GCT   
  0.00   0.11  70.66   1.59  95.63  87.15     31    0.410     0    0.000     -        -    0.410

2. -XX:+UseParNewGC

新生代使用ParNew垃圾回收器,老年代使用串行收集器
ParNew是串行收集器的多线程版本,只工作在新生代(可以见名知义,ParNew:Paralle New,并行新生代)。可以考虑在CPU并发能力强的系统中使用ParNew,单CPU的话,性能不一定比串行收集器好。

3. -XX:+UseParallelGC

新生代使用ParallelGC回收器,老年代使用串行收集器
ParallelGC是工作在新生代的并行垃圾收集器,使用复制算法。只是它更关注系统的吞吐量,所以和ParNewGC的一个区别是有相关参数可以设置GC的停顿时间。

例:jdk11 -Xmx2G -Xms2G -Xmn1G -XX:MetaspaceSize=128M -XX:MaxMetaspaceSize=128M -XX:+UseParallelGC

当有具体大小定义时(-Xmn),那么-XX:NewRatio比例设置将失效
默认的SurvivorRatio=8不生效的,自适应大小策略,显式指定SurvivorRatio,则会生效

JDK 1.8 默认使用 UseParallelGC 垃圾回收器,该垃圾回收器默认启动了 AdaptiveSizePolicy(自适应大小策略),会根据GC的情况自动计算计算 Eden、From 和 To 区的大小

因为ParallelScavenge系的GC最初设计就是默认打开AdaptiveSizePolicy的,它会自动、自适应的调整各种参数,只有Throughput收集器(ParallelOldGC,ParallelGC)才支持自适应大小调整,尝试在非Throughput收集器上启用或禁用自适应大小调整都不会有任何效果,即这种操作是空操作。

由于AdaptiveSizePolicy会动态调整 Eden、Survivor 的大小,有些情况存在Survivor 被自动调为很小,比如十几MB甚至几MB的可能,这个时候YGC回收掉 Eden区后,还存活的对象进入Survivor 装不下,就会直接晋升到老年代,导致老年代占用空间逐渐增加,从而触发FULL GC,如果一次FULL GC的耗时很长(比如到达几百毫秒),那么在要求高响应的系统就是不可取的。

jvm 参数:

zxm@zxm-pc:~$ jinfo -flags 31538
VM Flags:
-XX:-BytecodeVerificationLocal -XX:-BytecodeVerificationRemote -XX:CICompilerCount=4 -XX:CompressedClassSpaceSize=125829120 -XX:InitialHeapSize=2147483648 -XX:+ManagementServer -XX:MaxHeapSize=2147483648 -XX:MaxMetaspaceSize=134217728 -XX:MaxNewSize=1073741824 -XX:MetaspaceSize=134217728 -XX:MinHeapDeltaBytes=524288 -XX:NewSize=1073741824 -XX:NonNMethodCodeHeapSize=7549744 -XX:NonProfiledCodeHeapSize=244108496 -XX:OldSize=1073741824 -XX:ProfiledCodeHeapSize=0 -XX:ReservedCodeCacheSize=251658240 -XX:+SegmentedCodeCache -XX:TieredStopAtLevel=1 -XX:+UseCompressedClassPointers -XX:+UseCompressedOops -XX:+UseParallelGC

jvm 堆:

zxm@zxm-pc:~$ jhsdb jmap --heap --pid 31538
Attaching to process ID 31538, please wait...
Debugger attached successfully.
Server compiler detected.
JVM version is 11.0.9+7-LTS

using thread-local object allocation.
Parallel GC with 8 thread(s)

Heap Configuration:
   MinHeapFreeRatio         = 0
   MaxHeapFreeRatio         = 100
   MaxHeapSize              = 2147483648 (2048.0MB)
   NewSize                  = 1073741824 (1024.0MB)
   MaxNewSize               = 1073741824 (1024.0MB)
   OldSize                  = 1073741824 (1024.0MB)
   NewRatio                 = 2
   SurvivorRatio            = 8
   MetaspaceSize            = 134217728 (128.0MB)
   CompressedClassSpaceSize = 125829120 (120.0MB)
   MaxMetaspaceSize         = 134217728 (128.0MB)
   G1HeapRegionSize         = 0 (0.0MB)

Heap Usage:
PS Young Generation
Eden Space:
   capacity = 1018167296 (971.0MB)
   used     = 449854912 (429.01507568359375MB)
   free     = 568312384 (541.9849243164062MB)
   44.18280903023623% used
From Space:
   capacity = 28311552 (27.0MB)
   used     = 327680 (0.3125MB)
   free     = 27983872 (26.6875MB)
   1.1574074074074074% used
To Space:
   capacity = 27262976 (26.0MB)
   used     = 0 (0.0MB)
   free     = 27262976 (26.0MB)
   0.0% used
PS Old Generation
   capacity = 1073741824 (1024.0MB)
   used     = 19617432 (18.708641052246094MB)
   free     = 1054124392 (1005.2913589477539MB)
   1.8270157277584076% used

查看gc:

zxm@zxm-pc:~$ jstat -gc 31538
 S0C    S1C    S0U    S1U      EC       EU        OC         OU       MC     MU    CCSC   CCSU   YGC     YGCT    FGC    FGCT    CGC    CGCT     GCT   
27648.0 26624.0 320.0   0.0   994304.0 439311.4 1048576.0   19157.6   39168.0 37411.9 5120.0 4483.3     16    0.105   0      0.000   -          -    0.105
zxm@zxm-pc:~$ jstat -gcutil 31538
  S0     S1     E      O      M     CCS    YGC     YGCT    FGC    FGCT    CGC    CGCT     GCT   
  1.16   0.00  44.18   1.83  95.52  87.56     16    0.105     0    0.000     -        -    0.105

4. -XX:+UseParallelOldGC

新生代使用ParallelGC,老年代使用ParallelOldGC
ParallelOldGC是ParallelGC的老年代版本,只工作在老年代,使用标记压缩的回收算法,JDK1.6版本及之后支持

例:jdk11 -Xmx2G -Xms2G -Xmn1G -XX:MetaspaceSize=128M -XX:MaxMetaspaceSize=128M -XX:+UseParallelGC

jvm 参数:

zxm@zxm-pc:~$ jinfo -flags 458
VM Flags:
-XX:-BytecodeVerificationLocal -XX:-BytecodeVerificationRemote -XX:CICompilerCount=4 -XX:CompressedClassSpaceSize=125829120 -XX:InitialHeapSize=2147483648 -XX:+ManagementServer -XX:MaxHeapSize=2147483648 -XX:MaxMetaspaceSize=134217728 -XX:MaxNewSize=1073741824 -XX:MetaspaceSize=134217728 -XX:MinHeapDeltaBytes=524288 -XX:NewSize=1073741824 -XX:NonNMethodCodeHeapSize=7549744 -XX:NonProfiledCodeHeapSize=244108496 -XX:OldSize=1073741824 -XX:ProfiledCodeHeapSize=0 -XX:ReservedCodeCacheSize=251658240 -XX:+SegmentedCodeCache -XX:TieredStopAtLevel=1 -XX:+UseCompressedClassPointers -XX:+UseCompressedOops -XX:+UseParallelOldGC

jvm 堆:

zxm@zxm-pc:~$ jhsdb jmap --heap --pid 458
Attaching to process ID 458, please wait...
Debugger attached successfully.
Server compiler detected.
JVM version is 11.0.9+7-LTS

using thread-local object allocation.
Parallel GC with 8 thread(s)

Heap Configuration:
   MinHeapFreeRatio         = 0
   MaxHeapFreeRatio         = 100
   MaxHeapSize              = 2147483648 (2048.0MB)
   NewSize                  = 1073741824 (1024.0MB)
   MaxNewSize               = 1073741824 (1024.0MB)
   OldSize                  = 1073741824 (1024.0MB)
   NewRatio                 = 2
   SurvivorRatio            = 8
   MetaspaceSize            = 134217728 (128.0MB)
   CompressedClassSpaceSize = 125829120 (120.0MB)
   MaxMetaspaceSize         = 134217728 (128.0MB)
   G1HeapRegionSize         = 0 (0.0MB)

Heap Usage:
PS Young Generation
Eden Space:
   capacity = 1019740160 (972.5MB)
   used     = 239262440 (228.1784439086914MB)
   free     = 780477720 (744.3215560913086MB)
   23.46307906516107% used
From Space:
   capacity = 26738688 (25.5MB)
   used     = 294912 (0.28125MB)
   free     = 26443776 (25.21875MB)
   1.1029411764705883% used
To Space:
   capacity = 25690112 (24.5MB)
   used     = 0 (0.0MB)
   free     = 25690112 (24.5MB)
   0.0% used
PS Old Generation
   capacity = 1073741824 (1024.0MB)
   used     = 19404088 (18.50518035888672MB)
   free     = 1054337736 (1005.4948196411133MB)
   1.8071465194225311% used

查看gc:

zxm@zxm-pc:~$ jstat -gc 458
 S0C    S1C    S0U    S1U      EC       EU        OC         OU       MC     MU    CCSC   CCSU   YGC     YGCT    FGC    FGCT    CGC    CGCT     GCT   
25088.0 26112.0  0.0   288.0  995840.0 233675.0 1048576.0   18949.3   39168.0 37438.8 5120.0 4488.9     17    0.079   0      0.000   -          -    0.079
zxm@zxm-pc:~$ jstat -gcutil 458
  S0     S1     E      O      M     CCS    YGC     YGCT    FGC    FGCT    CGC    CGCT     GCT   
  0.00   1.10  23.46   1.81  95.59  87.67     17    0.079     0    0.000     -        -    0.079

5. -XX:+UseConcMarkSweepGC

新生代使用ParNew,老年代使用CMS

5.1 收集器步骤

CMS收集器的收集过程比以往的收集器都要复杂,收集过程分为四个步骤:初始标记、并发标记、重新标记、并发清除

CMS垃圾收集器更关注系统停顿时间,采用标记清除算法并且使用多线程并行回收。
CMS在GC失败时,老年代会使用串行收集器

CMS收集器是基于“标记-清除”算法实现的,特别提醒由于CMS是标记清除算法实现的所以是存在碎片问题的,CMS-initial-mark和CMS-remark会stop-the-world

5.2 jvm信息

例:jdk11 -Xmx2G -Xms2G -Xmn1G -XX:MetaspaceSize=128M -XX:MaxMetaspaceSize=128M -XX:+UseConcMarkSweepGC

当有具体大小定义时(-Xmn),那么-XX:NewRatio比例设置将失效
由SurvivorRatio=8,则from:to:eden=1:1:8

jvm 参数:

zxm@zxm-pc:~$ jinfo -flags 2262
VM Flags:
-XX:-BytecodeVerificationLocal -XX:-BytecodeVerificationRemote -XX:CICompilerCount=4 -XX:CompressedClassSpaceSize=125829120 -XX:InitialHeapSize=2147483648 -XX:+ManagementServer -XX:MaxHeapSize=2147483648 -XX:MaxMetaspaceSize=134217728 -XX:MaxNewSize=1073741824 -XX:MaxTenuringThreshold=6 -XX:MetaspaceSize=134217728 -XX:MinHeapDeltaBytes=196608 -XX:NewSize=1073741824 -XX:NonNMethodCodeHeapSize=7549744 -XX:NonProfiledCodeHeapSize=244108496 -XX:OldSize=1073741824 -XX:ProfiledCodeHeapSize=0 -XX:ReservedCodeCacheSize=251658240 -XX:+SegmentedCodeCache -XX:TieredStopAtLevel=1 -XX:+UseCompressedClassPointers -XX:+UseCompressedOops -XX:+UseConcMarkSweepGC

jvm 堆:

zxm@zxm-pc:~$ jhsdb jmap --heap --pid 2262
Attaching to process ID 2262, please wait...
Debugger attached successfully.
Server compiler detected.
JVM version is 11.0.9+7-LTS

using thread-local object allocation.
Concurrent Mark-Sweep GC

Heap Configuration:
   MinHeapFreeRatio         = 40
   MaxHeapFreeRatio         = 70
   MaxHeapSize              = 2147483648 (2048.0MB)
   NewSize                  = 1073741824 (1024.0MB)
   MaxNewSize               = 1073741824 (1024.0MB)
   OldSize                  = 1073741824 (1024.0MB)
   NewRatio                 = 2
   SurvivorRatio            = 8
   MetaspaceSize            = 134217728 (128.0MB)
   CompressedClassSpaceSize = 125829120 (120.0MB)
   MaxMetaspaceSize         = 134217728 (128.0MB)
   G1HeapRegionSize         = 0 (0.0MB)

Heap Usage:
New Generation (Eden + 1 Survivor Space):
   capacity = 966393856 (921.625MB)
   used     = 335509592 (319.96688079833984MB)
   free     = 630884264 (601.6581192016602MB)
   34.717686781319934% used
Eden Space:
   capacity = 859045888 (819.25MB)
   used     = 335285488 (319.75315856933594MB)
   free     = 523760400 (499.49684143066406MB)
   39.029985788139875% used
From Space:
   capacity = 107347968 (102.375MB)
   used     = 224104 (0.21372222900390625MB)
   free     = 107123864 (102.1612777709961MB)
   0.2087640820550977% used
To Space:
   capacity = 107347968 (102.375MB)
   used     = 0 (0.0MB)
   free     = 107347968 (102.375MB)
   0.0% used
concurrent mark-sweep generation:
   capacity = 1073741824 (1024.0MB)
   used     = 20417280 (19.471435546875MB)
   free     = 1053324544 (1004.528564453125MB)
   1.9015073776245117% used

查看gc:

zxm@zxm-pc:~$ jstat -gc 2262
 S0C    S1C    S0U    S1U      EC       EU        OC         OU       MC     MU    CCSC   CCSU   YGC     YGCT    FGC    FGCT    CGC    CGCT     GCT   
104832.0 104832.0  0.0   218.9  838912.0 327435.8 1048576.0   19938.8   39168.0 37444.4 5120.0 4492.1     17    0.139   0      0.000   0      0.000    0.139
zxm@zxm-pc:~$ jstat -gcutil 2262
  S0     S1     E      O      M     CCS    YGC     YGCT    FGC    FGCT    CGC    CGCT     GCT   
  0.00   0.21  39.03   1.90  95.60  87.74     17    0.139     0    0.000     0    0.000    0.139

6. -XX:+UseG1GC

使用G1回收器,作用于新生代和老年代

6.1 收集器步骤

虽然回收的范围是整个堆,但还是有分代回收的回收方式。在年轻代依然采用复制算法;年老代也同样采用“标记-清除-整理”算法。但是,新生代与老年代在堆内存中的布局就和以往的收集器有着很大的区别:G1将整个堆分成了一个个大小相等的独立区域,叫做region。其中依然保存着新生代和年老代

他的回收过程也分为四个部分:初始标记、并发标记、最终标记、筛选回收。

和CMS收集器类似,初始标记需要STW;并发标记不需要;最终标记就是做一些小修改,需要STW;而筛选回收则有些不同,在众多的region中,每个region可回收的空间各不相同,但是回收所消耗的时间是需要控制的,不能太长,因此G1就会筛选出一些可回收空间比较大的region进行回收,这就是G1的优先回收机制。这也是保证了G1收集器能在有限的时间内能够获得最高回收效率的原因。通过-XX:MaxGCPauseMills=50毫秒设置有限的收集时间

每个Region的大小通过-XX:G1HeapRegionSize来设置,大小为1~32MB,默认最多可以有2048个Region,那么按照默认值计算G1能管理的最大内存就是32MB*2048=64G

6.2 jvm信息

例:jdk11 -Xmx2G -Xms2G -Xmn1G -XX:MetaspaceSize=128M -XX:MaxMetaspaceSize=128M -XX:+UseG1GC

jvm 参数:

zxm@zxm-pc:~$ jinfo -flags 3680
VM Flags:
-XX:-BytecodeVerificationLocal -XX:-BytecodeVerificationRemote -XX:CICompilerCount=4 -XX:CompressedClassSpaceSize=125829120 -XX:ConcGCThreads=2 -XX:G1ConcRefinementThreads=8 -XX:G1HeapRegionSize=1048576 -XX:GCDrainStackTargetSize=64 -XX:InitialHeapSize=2147483648 -XX:+ManagementServer -XX:MarkStackSize=4194304 -XX:MaxHeapSize=2147483648 -XX:MaxMetaspaceSize=134217728 -XX:MaxNewSize=1073741824 -XX:MetaspaceSize=134217728 -XX:MinHeapDeltaBytes=1048576 -XX:NewSize=1073741824 -XX:NonNMethodCodeHeapSize=7549744 -XX:NonProfiledCodeHeapSize=244108496 -XX:ProfiledCodeHeapSize=0 -XX:ReservedCodeCacheSize=251658240 -XX:+SegmentedCodeCache -XX:TieredStopAtLevel=1 -XX:+UseCompressedClassPointers -XX:+UseCompressedOops -XX:+UseG1GC 

jvm 堆:

zxm@zxm-pc:~$ jhsdb jmap --heap --pid 3680
Attaching to process ID 3680, please wait...
Debugger attached successfully.
Server compiler detected.
JVM version is 11.0.9+7-LTS

using thread-local object allocation.
Garbage-First (G1) GC with 8 thread(s)

Heap Configuration:
   MinHeapFreeRatio         = 40
   MaxHeapFreeRatio         = 70
   MaxHeapSize              = 2147483648 (2048.0MB)
   NewSize                  = 1073741824 (1024.0MB)
   MaxNewSize               = 1073741824 (1024.0MB)
   OldSize                  = 5452592 (5.1999969482421875MB)
   NewRatio                 = 2
   SurvivorRatio            = 8
   MetaspaceSize            = 134217728 (128.0MB)
   CompressedClassSpaceSize = 125829120 (120.0MB)
   MaxMetaspaceSize         = 134217728 (128.0MB)
   G1HeapRegionSize         = 1048576 (1.0MB)

Heap Usage:
G1 Heap:
   regions  = 2048
   capacity = 2147483648 (2048.0MB)
   used     = 620436912 (591.6947479248047MB)
   free     = 1527046736 (1456.3052520751953MB)
   28.891345113515854% used
G1 Young Generation:
Eden Space:
   regions  = 573
   capacity = 1127219200 (1075.0MB)
   used     = 600834048 (573.0MB)
   free     = 526385152 (502.0MB)
   53.30232558139535% used
Survivor Space:
   regions  = 1
   capacity = 1048576 (1.0MB)
   used     = 1048576 (1.0MB)
   free     = 0 (0.0MB)
   100.0% used
G1 Old Generation:
   regions  = 20
   capacity = 1019215872 (972.0MB)
   used     = 19602864 (18.694747924804688MB)
   free     = 999613008 (953.3052520751953MB)
   1.9233279758029513% used

查看gc:

zxm@zxm-pc:~$ jstat -gc 3680
 S0C    S1C    S0U    S1U      EC       EU        OC         OU       MC     MU    CCSC   CCSU   YGC     YGCT    FGC    FGCT    CGC    CGCT     GCT   
 0.0   1024.0  0.0   1024.0 1100800.0 587776.0  995328.0   19143.4   39168.0 37427.9 5120.0 4490.1     27    0.226   0      0.000   0      0.000    0.226
zxm@zxm-pc:~$ jstat -gcutil 3680
  S0     S1     E      O      M     CCS    YGC     YGCT    FGC    FGCT    CGC    CGCT     GCT   
  0.00 100.00  53.40   1.92  95.56  87.70     27    0.226     0    0.000     0    0.000    0.226