簡體   English   中英

為什么在 Java 中這種情況下原始數據類型比引用數據類型消耗更多內存?

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

我試圖檢查程序的內存消耗。 在檢查過程中,我注意到了一些有趣的事情。

我創建了一個 Load 類,其中包含一些字段。

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

我創建了 500000 個 Load 對象並將它們添加到 ArrayList。 我發現,它占用了大約18 MB的內存。

然后,我修改了 Load 類並使用reference類型 Long。

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 消耗更多的內存。

我的問題是,為什么在這種情況下原始類型需要更多內存?

內存測試代碼:

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

對於 32 位 JVM,以及具有CompressedOOPs功能(HotSpot JVM 支持並默認啟用)的 64 位 JVM,與long的 8 個字節相比,引用僅消耗 4 個字節。

即使使用實際對象初始化引用,共享對象時也可能消耗較少的內存。 這適用於常量的自動裝箱

如果被裝箱的值 p 是對booleanbytecharshortintlong類型的常量表達式(第15.29 節)求boolean的結果,並且結果為truefalse ,則為'\'范圍內的字符到'\' ,或-128127范圍內的整數,然后讓abp的任何兩個裝箱轉換的結果。 總是a == b的情況。

但也適用於所有以Long.valueOf(long)結束的操作。

此方法將始終緩存 -128 到 127(含)范圍內的值,並且可能緩存此范圍之外的其他值。

當然,如果你創建了很多非共享的Long對象,它們會比原始long消耗更多的內存。 如果您使用許多不同的值,即使是潛在的共享它們也無濟於事。

  • 最大的primitive可以容納8 bytes

  • 每個對象至少有12 bytes (默認 64 位虛擬機和相對較小的堆)的標頭。 自動使它比原始大。

我知道有一個很好的庫可以正確執行,稱為jol 這是一個相關的問題。

設置jol運行示例以了解您感興趣的實際數字相當容易。

由於“Long id”未初始化,因此它僅占用 4/8 字節(取決於 VM 和操作系統),其中“long id”默認使用 0L 初始化,占用 8 個字節。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM