-XX:PretenureSizeThreshold的預設值和作用淺析

liuxuhui發表於2021-09-09

一、背景

《深入理解Java虛擬機器》第93頁,3.6.2 大物件直接進入老年代。

講到大物件主要指字串和陣列,虛擬機器提供了一個-XX:PretenureSizeThreshold引數,大於這個值的引數直接在老年代分配。

這樣做的目的是避免在Eden區和兩個Survivor區之間發生大量的記憶體複製(新生代採用複製演算法)。

但是這裡沒講清楚預設值是多少,預設會不會“大”物件直接進入老年代。

二、解析

2.1 參考文章

找到了一篇相關問題的文章《Frequently Asked Questions about Garbage Collection in the HotspotTM JavaTM Virtual Machine》

第29條:Do objects ever get allocated directly into the old generation?

In 1.4.1 there two situations where allocation may occur directly into the old generation.

有兩種情況,物件會直接分配到老年代。
If an allocation fails in the young generation and the object is a large array that does not contain any references to objects, it can be allocated directly into the old generation. In some select instances, this strategy was intended to avoid a collection of the young generation by allocating from the old generation.

如果在新生代分配失敗且物件是一個不含任何物件引用的大陣列,可被直接分配到老年代。

透過在老年代的分配避免新生代的一次垃圾回收。


There is a flag (available in 1.4.2 and later) l-XX:PretenureSizeThreshold=<byte size> that can be set to limit the size of allocations in the young generation. Any allocation larger than this will not be attempted in the young generation and so will be allocated out of the old generation.

XX:PretenureSizeThreshold=<位元組大小>可以設分配到新生代物件的大小限制。

任何比這個大的物件都不會嘗試在新生代分配,將在老年代分配記憶體。


The threshold size for 1) is 64k words. The default size for PretenureSizeThreshold is 0 which says that any size can be allocated in the young generation.

PretenureSizeThreshold 預設值是0,意味著任何物件都會現在新生代分配記憶體。

2.2 實驗解析

設定虛擬機器引數

-Xms2048m
-Xmx2048m
-Xmn1024m
-XX:+UseConcMarkSweepGC

-XX:SurvivorRatio=8

-Xms表示初始化堆記憶體

-Xmx 表示最大堆記憶體

-Xmn表示新生代的記憶體

-XX:SurvivorRatio=8表示新生代的Eden佔8/10,S1和S2各佔1/10.

因此Eden的記憶體大小為:0.8*1024*1024*1024位元組 約為819**1024*1024

上程式碼

public class Test {    public static void main(String[] args) throws Exception {        byte[] array = new byte[700 * 1024 * 1024];//734003216

        for (MemoryPoolMXBean memoryPoolMXBean : ManagementFactory.getMemoryPoolMXBeans()) {

            System.out.println(memoryPoolMXBean.getName() + "  總量:" + memoryPoolMXBean.getUsage().getCommitted() + "   使用的記憶體:" + memoryPoolMXBean.getUsage().getUsed());
        }

    }
}


可以看到應該被分配到了新生代的Eden區

Code Cache  總量:2555904   使用的記憶體:1206528
Metaspace  總量:4980736   使用的記憶體:3426872
Compressed Class Space  總量:524288   使用的記憶體:371280
Par Eden Space  總量:859045888   使用的記憶體:785546016
Par Survivor Space  總量:107347968   使用的記憶體:0
CMS Old Gen  總量:1073741824   使用的記憶體:0


將陣列物件增加到大於Eden區記憶體大小

byte[] array = new byte[900 * 1024 * 1024];

會導致新生代分配失敗,直接進入老年代

Code Cache  總量:2555904   使用的記憶體:1197824
Metaspace  總量:4980736   使用的記憶體:3426024
Compressed Class Space  總量:524288   使用的記憶體:371280
Par Eden Space  總量:859045888   使用的記憶體:34361864
Par Survivor Space  總量:107347968   使用的記憶體:0
CMS Old Gen  總量:1073741824   使用的記憶體:943718416

且此時透過jstat -gcutil vmpid命令檢視垃圾回收狀態,發現並沒有YGC.

然後我們將位元組陣列改回 小於Eden去記憶體

byte[] array = new byte[700 * 1024 * 1024];

並新增啟動引數-XX:PretenureSizeThreshold=100000000

Code Cache  總量:2555904   使用的記憶體:1172160
Metaspace  總量:4980736   使用的記憶體:3412552
Compressed Class Space  總量:524288   使用的記憶體:371280
Par Eden Space  總量:859045888   使用的記憶體:51542800
Par Survivor Space  總量:107347968   使用的記憶體:0
CMS Old Gen  總量:1073741824   使用的記憶體:734003216

發現即使新生代足夠分配,大於這個值的大物件也直接在老年代分配。

從而印證了文件的說法。

三、總結

多查權威參考文件。

多試驗看效果


來自 “ ITPUB部落格 ” ,連結:http://blog.itpub.net/756/viewspace-2823182/,如需轉載,請註明出處,否則將追究法律責任。

相關文章