OpenJDK 17堆内存问题

4

我的代码:

public class Memory {
// Dummy Entity representing usual data objects
private static class Entity {
    public String name;
    public String detail;
    public Double amount;
    public Integer age;
}

// Linked list offers table inserts and helps illustrating the issue by using multiple
// references per entry
public static java.util.LinkedList<Entity> entities = new java.util.LinkedList<>();

// This threshold should be 2 times Xmn. It ensures the loop stops before a full GC happens.
private static final int MB = 1024 * 1024;
private static final int THRESHOLD = 100 * MB;

public static void main(String[] args) {
    System.out.println("Total Memory (in bytes): " + Runtime.getRuntime().totalMemory());
    System.out.println("Free Memory (in bytes): " + Runtime.getRuntime().freeMemory());
    System.out.println("Max Memory (in bytes): " + Runtime.getRuntime().maxMemory());

    while (true) {
        appendEntitiesToDataStructure();
        terminateBeforeFullGCorOOMEcanHappen();
    }
}

private static void appendEntitiesToDataStructure() {
    entities.add(new Entity());
}

private static void terminateBeforeFullGCorOOMEcanHappen() {
    if (Runtime.getRuntime().freeMemory() < THRESHOLD) {
        System.out.println("Elements created and added to LinkedList: " + entities.size());
        System.exit(0);
    }
}}

启动:

java -Xms${i}g -Xmx${i}g -Xmn50m memory.java >> output.txt

想法:我想了解JVM堆大小在演变时的行为。

堆大小:从1到100 GB

测试的GC:g1c,zgc,pgc

结果:使用g1c和pgc时,从32GB开始存在回归->随着堆大小的增加,我可以存储更少的数据在我的对象中。

模式:X:我的对象的大小;Y:堆大小

我不知道如何解释这种行为->有什么想法吗?

java g1gc java gpc java zgc


6
如果我没记错的话,32GB堆是JVM关闭压缩指针的时候(简单来说:64位JVM中的32位引用)。很可能这就是你看到的原因。 - Joachim Sauer
我在ZGC中没有看到这种行为,因为它没有压缩oops。 - sacha.p
2
这就是你的图表所显示的,快速的谷歌搜索也证实了这至少曾经是正确的(在JDK 15中)。我怀疑它现在仍然是正确的。 - Joachim Sauer
1个回答

3

请参考这篇文章:https://www.baeldung.com/jvm-compressed-oops

首先,让我们了解一下什么是oop:

HotSpot JVM使用一种称为oops或普通对象指针的数据结构来表示对象。这些oops相当于本地C指针。instanceOops是一种特殊的oop,用于表示Java中的对象实例。此外,JVM还支持一些其他oops,这些oops保存在OpenJDK源代码树中。

此外

enter image description here

一个非常棒的特性是将oops压缩到32位:
事实证明,JVM可以通过压缩对象指针或oops来避免浪费内存,因此我们可以拥有最佳方案:在64位机器上使用32位引用允许超过4 GB的堆空间!
最后:
为了启用oop压缩,我们可以使用-XX:+UseCompressedOops调优标志。自Java 7以来,当最大堆大小小于32 GB时,oop压缩是默认行为。当最大堆大小超过32 GB时,JVM将自动关闭oop压缩。因此,在32 Gb堆大小之外的内存利用需要单独管理。
所以,你的问题的答案是:你只是简单地越过了32 GB作为最大堆大小的限制。
证据:
查看您的图表并注意到我们正在讨论的行为发生在32 GB的级别。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接