问题
非移动GC一定比移动GC好吗?
基础知识
移动GC和非移动GC
移动GC
在进行垃圾回收时,为了减少碎片而移动对象来顺利完成垃圾回收的GC。
Serial GC
在单线程环境下,它在标记-清除(Mark-Sweep)算法的基础上进行,并且在执行垃圾收集时,会暂停所有应用线程。虽然它不是移动对象,但在某些情况下,它会移动对象以减少碎片。
Parallel GC
在多线程环境下,它通过多个线程并行执行大多数垃圾收集工作,以提高性能。在某些实现中,它可能包括对象移动以减少碎片。
非移动GC
在进行垃圾回收时,不移动对象,通过复制对象或者采用不同的策略来管理和回收内存。
G1 GC
G1将堆内存分割成多个区域,并优先处理垃圾最多的区域。G1不移动对象,而是通过复制存活对象到未使用的区域来处理内存回收。
CMS GC
在需要快速响应的应用场景下,CMS在应用程序运行的同时进行标记和清除工作,但不移动对象。
ZGC
采用多重映射和读屏障,指针染色,并发重映射,转发表,NUMA支持等技术来管理和回收内存。
Shenandoah
采用并发执行,大堆等技术支持来管理和回收内存。
GC的局部性
GC进行垃圾回收时,会保持内存的局部性,从而保证GC运行的效率。
时间局部性
如果一个信息项正在被访问,那么在近期它很可能还会被再次访问。程序循环、堆栈等是产生时间局部性的原因。
空间局部性
在最近的将来将用到的信息很可能与正在使用的信息在空间地址上是临近的。
顺序局部性
在典型程序中,除转移类指令外,大部分指令是顺序进行的。指令的顺序执行、数组的连续存放等是产生顺序局部性的原因。
GC局部性原理
调整数据的访问顺序
优化循环逻辑和数组访问顺序,减少缓存冲突,提高缓存命中率。
二进制文件中代码段对齐
优化代码段的排列,使得部分代码段能集中在一个缓存中,提高代码段的缓存命中率。
实验
测试用例源码
import org.openjdk.jmh.annotations.*;
import org.openjdk.jmh.infra.*;
@State(Scope.Thread)
@BenchmarkMode(Mode.AverageTime)
@OutputTimeUnit(TimeUnit.MICROSECONDS)
@Warmup(iterations = 5, time = 1, timeUnit = TimeUnit.SECONDS)
@Measurement(iterations = 5, time = 1, timeUnit = TimeUnit.SECONDS)
@Fork(value = 1, jvmArgsAppend = {
"-Xms8g", "-Xmx8g", "-Xmn7g" })
public class ArrayWalkBench {
@Param({
"16", "256", "4096", "65536", "1048576", "16777216"})
int size;
@Param(