特点

JDK11中推出。
GC 停顿时间很短,不分代(在JDK21中引入了分代模式)。

怎么分配内存的

内存块分为三种类型:

  • small:2MB,存放小于256KB的对象
  • medium:32MB,存放[256KB,4MB)的对象
  • large:大小不固定,2*n MB(n>=2)。为单一对象单独分配,存放大于等于4MB的对象

ZGC 使用64位的地址,不允许开启指针压缩。JVM的指针压缩是指,使用32位地址实现更大容量的内存管理。比如32位原本能表示2^32 byte的内存,每个地址表示一个byte,指针压缩就是让一个地址表示多个byte,并且在对象不是整数倍的情况给对象加上padding。

ZGC的地址是18位未使用+4位标记+42位对象地址。(称为染色指针,42位地址能管理最多4TB,ZGC也支持43和44的)。而其它GC gc相关标记是放在对象头的markword中的。

4位标记分别是:

  • finalizable
  • remapped
  • marked1
  • marked2

remapped、marked1、marked2是指针的三种颜色。

当创建一个对象后,它会有三个地址,分别是这三种颜色。

当从堆中读取一个引用的时候(例如读取某个对象的某个引用字段),JVM会插入load barrier,返回实际的引用,因为这个引用所指向的对象可能在gc过程中被移动了。

怎么回收内存的

  • 初始标记(STW): 标记 GC Roots
  • 并发标记:初始状态下对象都是remapped的地址,当并发标记中如果对象的引用发生变化,那地址使用的是marked。所以这个阶段结束后marked的对象都是活跃对象。
  • 再标记(STW):
  • 并发转移准备:确定需要转移的区块
  • 初始转移(STW):转移GC Roots的对象
  • 并发转移:如果对象被访问过,那么标记为remapped

相关的 JVM 参数

ZGC的参数简单。

  • 堆大小: -Xmx。当分配速率过高,超过回收速率,造成堆内存不够用时,会触发Allocation Stall,这个类Stall会减缓当前的用户线程。因此,当我们在GC日志中看到Allocation Stall,通常可以认为堆空间偏小或者concurrent gc threads数偏小。
  • GC触发时机: ZAllocatioSpike Tolerance,ZCollectionInterval。ZAllocationSpikeTolerance 用来估算当前的堆内存分配速率,在当前剩余的堆内存下,ZAllocationSpike Tolerance越大,估算的达到OOM的时间越快,ZGC就会更早地进行触发GC。ZCollectionInterval用来指定GC发生的间隔,以秒位单位触发GC
  • GC线程: ParallerGCThreads,ConcGCThreads。ParallelGCThreads是设置STW任务的GC线程数,默认为CPU个数的60%;ConcGCThreads是并发阶段GC线程的数目,默认为CPU个数的12.5%。

参考

https://javadoop.com/post/zgc

https://mikechen.cc/16719.html

http://www.lihuibin.top/archives/a87613ac/#ZGC%E8%AF%9E%E7%94%9F%E8%83%8C%E6%99%AF