首页 > 解决方案 > 在 Java 中,为什么原始数据类型比引用数据类型消耗更多内存?

问题描述

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

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

class Load {
    String name;
    String title;
    long id;
}

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

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

class Load {
    String name;
    String title;
    Long id;
}

再次创建了 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

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

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

内存测试代码:

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));
    }
}

完整代码git repo

标签: javamemoryjvmprimitive-types

解决方案


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

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

如果被装箱的值 p 是对, , , ,或类型的常量表达式 ( §15.29 ) 求值的结果,并且结果是, , 包含范围内的字符或包含范围内的整数,然后让和成为 的任意两次装箱转换的结果。总是如此。booleanbytecharshortintlongtruefalse'\u0000''\u007f'-128127abpa == b

但也适用于所有最终的操作Long.valueOf(long)

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

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


推荐阅读