简体   繁体   English

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

[英]Why does primitive data type consume much memory than reference data type for this scenario in Java?

I tried to check the memory consumption of a program.我试图检查程序的内存消耗。 During the check, I have noticed some interesting things.在检查过程中,我注意到了一些有趣的事情。

I created a Load class, which contains some fields.我创建了一个 Load 类,其中包含一些字段。

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

I created 500000 Load objects and add them to an ArrayList.我创建了 500000 个 Load 对象并将它们添加到 ArrayList。 I have found, it took around 18 MB of memory.我发现,它占用了大约18 MB的内存。

Then, I modified the Load class and use reference type Long.然后,我修改了 Load 类并使用reference类型 Long。

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

Again created 500000 Load objects and add them to ArrayList.再次创建了 500000 个 Load 对象并将它们添加到 ArrayList。 Interestingly this time it took less memory than the previous one.有趣的是,这一次它占用的内存比前一次少。 It way 14 MB.它的方式是 14 MB。

Run test changing os and JVM version.运行测试更改操作系统和 JVM 版本。 Found the following results.找到以下结果。

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

Surprisingly, in all of these test runs, Object contains primitive types long consume more memory than Object contains reference Long.令人惊讶的是,在所有这些测试运行中,包含原始类型的 Object 比包含引用 Long 的 Object 消耗更多的内存。

My question is, why primitive type takes more memory in this scenario?我的问题是,为什么在这种情况下原始类型需要更多内存?

Memory Tester Code:内存测试代码:

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

Complete code git repo .完整代码git repo

For 32 Bit JVMs, but also for 64 Bit JVMs with the CompressedOOPs feature (which is supported by the HotSpot JVM and on by default), a reference consumes only 4 bytes, compared to the 8 bytes of a long .对于 32 位 JVM,以及具有CompressedOOPs功能(HotSpot JVM 支持并默认启用)的 64 位 JVM,与long的 8 个字节相比,引用仅消耗 4 个字节。

Even when you initialize the reference with an actual object, it may consume less memory when the object is shared .即使使用实际对象初始化引用,共享对象时也可能消耗较少的内存。 This applies to autoboxing of constants:这适用于常量的自动装箱

If the value p being boxed is the result of evaluating a constant expression ( §15.29 ) of type boolean , byte , char , short , int , or long , and the result is true , false , a character in the range '\' to '\' inclusive, or an integer in the range -128 to 127 inclusive, then let a and b be the results of any two boxing conversions of p .如果被装箱的值 p 是对booleanbytecharshortintlong类型的常量表达式(第15.29 节)求boolean的结果,并且结果为truefalse ,则为'\'范围内的字符到'\' ,或-128127范围内的整数,然后让abp的任何两个装箱转换的结果。 It is always the case that a == b .总是a == b的情况。

but also to all operations ending up at Long.valueOf(long) in general.但也适用于所有以Long.valueOf(long)结束的操作。

This method will always cache values in the range -128 to 127, inclusive, and may cache other values outside of this range.此方法将始终缓存 -128 到 127(含)范围内的值,并且可能缓存此范围之外的其他值。

Of course, if you create a lot of unshared Long objects, they will consume far more memory than the primitive long .当然,如果你创建了很多非共享的Long对象,它们会比原始long消耗更多的内存。 If you use a lot of distinct values, even a potential sharing of them wouldn't help.如果您使用许多不同的值,即使是潜在的共享它们也无济于事。

  • The largest primitive can hold 8 bytes .最大的primitive可以容纳8 bytes

  • every Object has at least 12 bytes (with default 64 bit VMs and relatively small heap) of headers.每个对象至少有12 bytes (默认 64 位虚拟机和相对较小的堆)的标头。 Automatically making it bigger than a primitive.自动使它比原始大。

There is a single good library that I am aware of that does correctly, called jol .我知道有一个很好的库可以正确执行,称为jol here is a related question. 这是一个相关的问题。

It is rather easy to get jol set-up and run the samples to understand the actual numbers you are interested in.设置jol运行示例以了解您感兴趣的实际数字相当容易。

由于“Long id”未初始化,因此它仅占用 4/8 字节(取决于 VM 和操作系统),其中“long id”默认使用 0L 初始化,占用 8 个字节。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM