当前位置: 首页 > article >正文

JVM常用概念之移动GC和局部性

问题

非移动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({"false", "true"})
    boolean shuffle;

    @Param({"false", "true"})
    boolean gc;

    String[] arr;

    @Setup
    public void setup() throws IOException, InterruptedException {
        arr = new String[size];
        for (int c = 0; c < size; c++) {
            arr[c] = "Value" + c;
        }
        if (shuffle) {
            Collections.shuffle(Arrays.asList(arr), new Random(0xBAD_BEE));
        }
        if (gc) {
            for (int c = 0; c < 5; c++) {
                System.gc();
                TimeUnit.SECONDS.sleep(1);
            }
        }
    }

    @Benchmark
    public void walk(Blackhole bh) {
        for (String val : arr) {
            bh.consume(val.hashCode());
        }
    }
}

  • 数组的大小。不同规模的数组大小更能凸显差异。
  • 打乱顺序。表示我们是否在遍历数组之前对其进行打乱。启用打乱可模拟插入不按顺序和/或随时间推移发生在不同索引上的情况。
  • 垃圾回收。表示在准备好数据集后强制执行 GC。由于工作负载未在有效负载代码中分配,因此我们需要明确强制执行 GC,否则它将永远不会运行。

运行结果

Benchmark             (gc)  (shuffle)    (size)  Mode  Cnt       Score      Error  Units

ArrayWalkBench.walk  false      false        16  avgt    5       0.051 ±    0.001  us/op
ArrayWalkBench.walk  false      false       256  avgt    5       0.821 ±    0.001  us/op
ArrayWalkBench.walk  false      false      4096  avgt    5      14.516 ±    0.026  us/op
ArrayWalkBench.walk  false      false     65536  avgt    5     307.210 ±    4.789  us/op
ArrayWalkBench.walk  false      false   1048576  avgt    5    4306.660 ±    7.950  us/op
ArrayWalkBench.walk  false      false  16777216  avgt    5   60561.476 ±  925.685  us/op

ArrayWalkBench.walk  false       true        16  avgt    5       0.051 ±    0.001  us/op
ArrayWalkBench.walk  false       true       256  avgt    5       0.823 ±    0.003  us/op
ArrayWalkBench.walk  false       true      4096  avgt    5      18.646 ±    0.044  us/op
ArrayWalkBench.walk  false       true     65536  avgt    5     461.187 ±   31.183  us/op
ArrayWalkBench.walk  false       true   1048576  avgt    5   16350.706 ±   75.757  us/op
ArrayWalkBench.walk  false       true  16777216  avgt    5  312296.960 ±  632.552  us/op

ArrayWalkBench.walk   true      false        16  avgt    5       0.051 ±    0.001  us/op
ArrayWalkBench.walk   true      false       256  avgt    5       0.820 ±    0.004  us/op
ArrayWalkBench.walk   true      false      4096  avgt    5      13.639 ±    0.063  us/op
ArrayWalkBench.walk   true      false     65536  avgt    5     174.475 ±    0.771  us/op
ArrayWalkBench.walk   true      false   1048576  avgt    5    4345.980 ±   15.230  us/op
ArrayWalkBench.walk   true      false  16777216  avgt    5   68687.192 ± 1375.171  us/op

ArrayWalkBench.walk   true       true        16  avgt    5       0.051 ±    0.001  us/op
ArrayWalkBench.walk   true       true       256  avgt    5       0.828 ±    0.010  us/op
ArrayWalkBench.walk   true       true      4096  avgt    5      13.749 ±    0.088  us/op
ArrayWalkBench.walk   true       true     65536  avgt    5     174.230 ±    0.655  us/op
ArrayWalkBench.walk   true       true   1048576  avgt    5    4365.162 ±   88.927  us/op
ArrayWalkBench.walk   true       true  16777216  avgt    5   70631.288 ± 1144.980  us/op

如上述运行结果可知,遍历打乱顺序的数组确实比初始的有序数组慢得多,这主要是有对象的内存布局决定的!并且发现在使用ParallelGC后,将内存的数组在内存进行重新排列,从而有利于提升局部性执行的效率。

Benchmark             (gc)  (shuffle)    (size)  Mode  Cnt       Score      Error  Units

ArrayWalkBench.walk  false      false        16  avgt    5       0.051 ±    0.001  us/op
ArrayWalkBench.walk  false      false       256  avgt    5       0.826 ±    0.006  us/op
ArrayWalkBench.walk  false      false      4096  avgt    5      14.556 ±    0.049  us/op
ArrayWalkBench.walk  false      false     65536  avgt    5     274.073 ±   37.163  us/op
ArrayWalkBench.walk  false      false   1048576  avgt    5    4383.223 ±  997.953  us/op
ArrayWalkBench.walk  false      false  16777216  avgt    5   60322.171 ±  266.683  us/op

ArrayWalkBench.walk  false       true        16  avgt    5       0.051 ±    0.001  us/op
ArrayWalkBench.walk  false       true       256  avgt    5       0.826 ±    0.007  us/op
ArrayWalkBench.walk  false       true      4096  avgt    5      18.169 ±    0.165  us/op
ArrayWalkBench.walk  false       true     65536  avgt    5     312.345 ±   26.149  us/op
ArrayWalkBench.walk  false       true   1048576  avgt    5   16445.739 ±   54.241  us/op
ArrayWalkBench.walk  false       true  16777216  avgt    5  311573.643 ± 3650.280  us/op

如上述运行结果可知,如果使用非移动的GC可能就没有这样的优势,从而导致非移动GC在这种情况下可能比移动GC进行垃圾回收的效率要低!

那出现这种结果的原因是什么呢?我们可以通过-prof perfnorm来进一步分析,执行结果如下:

Benchmark                    (gc)  (shuffle)   (size)  Mode  Cnt   Score    Error  Units

walk                         true       true  1048576  avgt   25   4.247 ±  0.090  ns/op
walk:CPI                     true       true  1048576  avgt    5   0.498 ±  0.050   #/op
walk:L1-dcache-load-misses   true       true  1048576  avgt    5   0.955 ±  0.025   #/op
walk:L1-dcache-loads         true       true  1048576  avgt    5  12.149 ±  0.432   #/op
walk:L1-dcache-stores        true       true  1048576  avgt    5   7.027 ±  0.176   #/op
walk:LLC-load-misses         true       true  1048576  avgt    5   0.156 ±  0.029   #/op
walk:LLC-loads               true       true  1048576  avgt    5   0.514 ±  0.014   #/op
walk:cycles                  true       true  1048576  avgt    5  17.056 ±  1.673   #/op
walk:instructions            true       true  1048576  avgt    5  34.223 ±  0.860   #/op

walk                        false       true  1048576  avgt   25  16.155 ±  0.101  ns/op
walk:CPI                    false       true  1048576  avgt    5   1.885 ±  0.069   #/op
walk:L1-dcache-load-misses  false       true  1048576  avgt    5   1.922 ±  0.076   #/op
walk:L1-dcache-loads        false       true  1048576  avgt    5  12.128 ±  0.326   #/op
walk:L1-dcache-stores       false       true  1048576  avgt    5   7.032 ±  0.212   #/op
walk:LLC-load-misses        false       true  1048576  avgt    5   1.031 ±  0.032   #/op
walk:LLC-loads              false       true  1048576  avgt    5   1.365 ±  0.101   #/op
walk:cycles                 false       true  1048576  avgt    5  64.700 ±  2.613   #/op
walk:instructions           false       true  1048576  avgt    5  34.335 ±  1.564   #/op

如上述运行结果所示,由于使用的是随机版本,内存的随机游走会消耗大量的时间成本,所以导致每条指令大约需要 2 个时钟,并且几乎每个最后一级缓存加载都会失败。

总结

垃圾回收的局部性与对象图的布局有关,而对象图的布局伴随着程序的运行是动态变化的,因此GC需要一直动态的适应这种变化的对象图的布局,从而保证局部性的优势,这在现实应用场景下是很难实现的,而进一步讲,如果考虑NUMA特性,其对于垃圾回收的局部性更是灾难,所以所以不考虑垃圾回收局部性就一味的推崇非移动垃圾回收器的说法是错误的。


http://www.kler.cn/a/579003.html

相关文章:

  • k8s下部署ansible进行node-export二安装
  • electron builder打包时,出现errorOut=ERROR: Cannot create symbolic link
  • 【AVRCP】协议深入解析(1):从框架到数据单元
  • 负载均衡集群和高可用集群
  • 【神经网络】0.深度学习基础:解锁深度学习,重塑未来的智能新引擎
  • chatgpt完成python提取PDF简历指定内容的案例
  • 【2025深夜随笔】简单认识一下Android Studio
  • kafka单机部署实战
  • 10分钟看明白爆火的本地知识库! 掌握本地知识库基本概念与RAG的完整架构
  • C++学习之格斗小游戏综合案例
  • 6-5,web3浏览器链接区块链(react+区块链实战)
  • 【已解决】AttributeError: module ‘numpy‘ has no attribute ‘object‘.
  • css实现元素垂直居中显示的7种方式
  • Vue、React、原生小程序的写法对比差异
  • 基于python大数据的招聘数据可视化与推荐系统
  • STM32 串口升级程序方法说明
  • 基于Spring Boot的学院商铺管理系统的设计与实现(LW+源码+讲解)
  • 微软云计算[2]之微软云关系数据库SQL Azure
  • 【新手指南】pyqt可视化远程部署deepseek7B蒸馏版模型
  • C++基于Crow的Web服务开发