为什么在 Java 中这种情况下原始数据类型比引用数据类型消耗更多内存?

Md.*_*bib 4 java memory jvm primitive-types

我试图检查程序的内存消耗。在检查过程中,我注意到了一些有趣的事情。

我创建了一个 Load 类,其中包含一些字段。

class Load {
    String name;
    String title;
    long id;
}
Run Code Online (Sandbox Code Playgroud)

我创建了 500000 个 Load 对象并将它们添加到 ArrayList。我发现,它占用18 MB了内存。

然后,我修改了 Load 类并使用了referenceLong 类型。

class Load {
    String name;
    String title;
    Long id;
}
Run Code Online (Sandbox Code Playgroud)

再次创建了 500000 个 Load 对象并将它们添加到 ArrayList。有趣的是,这一次它占用的内存比前一次少。它的方式是 14 MB。

运行测试更改操作系统和 JVM 版本。找到以下结果。

OS: Windows 10 Pro 64 bit
JDK: 11 64bit
 
Object Created  | Load Object         | Memory | Load Object         | Memory  
------------------------------------------------------------------------------
1. 500000       | With primitive long | 18 MB  | With reference Long | 14 MB
2. 900000       |                     | 32 MB  |                     | 26 MB
3. 1500000      |                     | 53 MB  |                     | 41 MB

OS: macOS Big Sur 64 bit
JDK: 8 64bit
 
Object Created  | Load Object         | Memory | Load Object         | Memory  
------------------------------------------------------------------------------
1. 500000       | With primitive long | 18 MB  | With reference Long | 14 MB
2. 900000       |                     | 32 MB  |                     | 26 MB
3. 1500000      |                     | 53 MB  |                     | 41 MB
Run Code Online (Sandbox Code Playgroud)

令人惊讶的是,在所有这些测试运行中,包含原始类型的 Object 比包含引用 Long 的 Object 消耗更多的内存。

我的问题是,为什么在这种情况下原始类型需要更多内存?

内存测试代码:

public class MemoryChecker {

    private static final long MEGABYTE = 1024L * 1024L;

    public static long bytesToMegabytes(long bytes) {
        return bytes / MEGABYTE;
    }

    public static void main(String[] args) {
        List<Load> list = new ArrayList<Load>();
        for (int i = 0; i <= 500000
                ; i++) {
            list.add(new Load("Jim", "Knopf", 11L));
        }
        // Get the Java runtime
        Runtime runtime = Runtime.getRuntime();
        // Run the garbage collector
        runtime.gc();
        // Calculate the used memory
        long memory = runtime.totalMemory() - runtime.freeMemory();
        System.out.println("Used memory is megabytes: " + bytesToMegabytes(memory));
    }
}
Run Code Online (Sandbox Code Playgroud)

完整代码git repo

Hol*_*ger 5

对于 32 位 JVM,以及具有CompressedOOPs功能(HotSpot JVM 支持并默认启用)的64 位 JVM ,引用仅消耗 4 个字节,而long.

即使您使用实际对象初始化引用,共享对象时它也可能消耗较少的内存。这适用于常量的自动装箱

如果值p被装箱是评估一个常量表达式(结果§15.29类型的)booleanbytecharshortint,或long,其结果是truefalse在的范围内的字符'\u0000',以'\u007f'包容,或在范围内的整数-128,以127包容,然后让ab成为 的任意两个装箱转换的结果p。情况总是如此a == b

但也适用Long.valueOf(long)于一般情况下的所有操作。

此方法将始终缓存 -128 到 127(含)范围内的值,并且可能缓存此范围之外的其他值。

当然,如果你创建了很多非共享Long对象,它们会比原始对象消耗更多的内存long。如果您使用许多不同的值,即使是潜在的共享它们也无济于事。