gpt4 book ai didi

java - 为什么在更新大型数组的元素时次要 GC 持续时间会发生如此大的变化?

转载 作者:塔克拉玛干 更新时间:2023-11-02 08:30:18 31 4
gpt4 key购买 nike

我有以下简单程序:

public class GCArrays {
public static void main(String[] args) {
Object[] bigArr = new Object[1 << 24];
Object[] smallArr = new Object[1 << 12];

bigArr[0x897] = new Object();
smallArr[0x897] = new Object();

for (int i = 0; i < 1e10; i++) {
smallArr[0x897] = new Object(); // (*)
//bigArr[0x897] = new Object();
}

// to prevent bigArr and smallArr from being garbage collected
bigArr[0x897] = new Object();
smallArr[0x897] = new Object();
}
}

当我使用 ParallelGC 作为年轻代的 GC 算法运行它时:

java -classpath . -XX:InitialHeapSize=4G -XX:MaxHeapSize=4G -XX:NewRatio=3 -XX:+PrintGC -XX:+PrintGCDetails -XX:+UseParallelGC -XX:+UseParallelOldGC GCArrays

我得到的平均暂停时间低于 1 毫秒:

[GC (Allocation Failure) [PSYoungGen: 1047584K->32K(1048064K)] 1113476K->65924K(4193792K), 0.0007385 secs] [Times: user=0.00 sys=0.00, real=0.00 secs]

但是,如果我将标有 (*) 的行更改为修改 bigArr 而不是 smallArr,则暂停时间会增加到 10 毫秒:

[GC (Allocation Failure) [PSYoungGen: 1047584K->32K(1048064K)] 1113468K->65916K(4193792K), 0.0101251 secs] [Times: user=0.00 sys=0.00, real=0.01 secs]

请注意,该程序仅修改数组的单个元素。然而,看起来 JVM 仍然会扫描整个数组以在次要收集期间查找 Activity 对象。我对较长 GC 暂停的解释是否正确?为什么在这种情况下只修改一个元素就需要扫描整个数组?

最佳答案

This article解释脏卡的概念及其在年轻 GC 中的作用。

在这两种情况下,旧空间中的单个内存地址都是“脏的”,因此是单卡。对于跨越多个卡片(512 字节 block )的引用数组对象,仅修改真正修改的索引子范围的卡片。

由于只有单卡被“弄脏”,GC只需要扫描对应的512字节内存。

-XX:+UseConcMarkSweepGC “smallArr”和“bigArr”版本都显示相似的时间。

-XX:+UseConcMarkSweepGC + smallArr

[GC (Allocation Failure) [ParNew: 419458K->2K(471872K), 0.0015320 secs] 485365K->65909K(996160K), 0.0015635 secs] 
[Times: user=0.00 sys=0.00, real=0.00 secs]

-XX:+UseConcMarkSweepGC + bigArr

[GC (Allocation Failure) [ParNew: 419458K->2K(471872K), 0.0020550 secs] 485365K->65909K(996160K), 0.0020885 secs] 
[Times: user=0.00 sys=0.00, real=0.00 secs]

尽管这 -XX:+UseParallelOldGC , 似乎 GC 必须扫描整个 "bigArr"

-XX:+ParallelOldGC + smallArr

[GC (Allocation Failure) [PSYoungGen: 522768K->16K(523520K)] 588691K->65939K(1047808K), 0.0009430 secs] 
[Times: user=0.00 sys=0.00, real=0.00 secs]

-XX:+ParallelOldGC + bigArr

[GC (Allocation Failure) [PSYoungGen: 522768K->16K(523008K)] 588687K->65935K(1047296K), 0.0149276 secs] 
[Times: user=0.03 sys=0.00, real=0.02 secs]

-XX:+ParallelOldGC + bigArr = new Object[1 << 25]

[GC (Allocation Failure) [PSYoungGen: 522768K->16K(523520K)] 654219K->131467K(1047808K), 0.0413473 secs]
[Times: user=0.09 sys=0.00, real=0.04 secs]

直观计数器ParallelOldGCConcMarkSweepGC正在使用非常相似的年轻 GC 算法的不同实现。

看起来像PSYoungGen缺少仅扫描对象数组脏部分的优化。

关于java - 为什么在更新大型数组的元素时次要 GC 持续时间会发生如此大的变化?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/58187596/

31 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com