JAVA之JDK在64位系統(tǒng)默認(rèn)開啟壓縮指針分析(請(qǐng)多多指正!)
Sun的HotSpot VM從JDK5開始會(huì)根據(jù)運(yùn)行環(huán)境來自動(dòng)設(shè)定VM的一些參數(shù)(ergonomics)。其中大家最熟悉的可能是它會(huì)自動(dòng)選擇client與server模式、堆的初始和最大大小等。事實(shí)上ergonomics會(huì)設(shè)置非常多的內(nèi)部參數(shù),包括自動(dòng)選擇GC算法、并行GC的線程數(shù)、GC的工作區(qū)分塊大小、對(duì)象晉升閾值等等。
Ergonomics相關(guān)的邏輯大都在hotspot/src/share/vm/runtime/arguments.cpp中,值得留意的是使用了FLAG_SET_ERGO()的地方。
于是我們可以留意一下幾個(gè)版本的HotSpot對(duì)UseCompressedOops參數(shù)的處理的差異:
HotSpot 16:
C++代碼
#ifdef _LP64 if (MaxHeapSize <= max_heap_for_compressed_oops()) { if (FLAG_IS_DEFAULT(UseCompressedOops)) { } } #endif // _LP64
HotSpot 17:
C++代碼
#ifndef ZERO #ifdef _LP64 if (MaxHeapSize <= max_heap_for_compressed_oops()) { #ifndef COMPILER1 if (FLAG_IS_DEFAULT(UseCompressedOops) && !UseG1GC) { } } #endif #endif // _LP64 #endif // !ZERO
HotSpot 19 / HotSpot 20:
C++代碼
#ifndef ZERO #ifdef _LP64 if (MaxHeapSize <= max_heap_for_compressed_oops()) { #ifndef COMPILER1 if (FLAG_IS_DEFAULT(UseCompressedOops) && !UseG1GC) { FLAG_SET_ERGO(bool, UseCompressedOops, true); } #endif } #endif // _LP64 #endif // !ZERO
(注:HotSpot VM的版本號(hào)與JDK的版本號(hào)之間的關(guān)系,請(qǐng)參考另一篇筆記:Sun/Oracle JDK、OpenJDK、HotSpot VM版本之間的對(duì)應(yīng)關(guān)系)
可以看到,UseCompressedOops參數(shù)從HotSpot 19開始終于開始受ergonomics控制,會(huì)在下述條件滿足的時(shí)候默認(rèn)開啟管道磁力泵:
1、是64位系統(tǒng)(#ifdef _LP64)并且不是client VM(#ifndef COMPILER1);
2、Java堆的最大大小不大于一個(gè)閾值(MaxHeapSize <= max_heap_for_compressed_oops());
3、沒有通過。hotspotrc或命令行參數(shù)手動(dòng)設(shè)定過UseCompressedOops參數(shù)的值;
4、沒有使用Garbage-First (G1) GC.
第1、3、4點(diǎn)都很直觀,于是第2點(diǎn)就是個(gè)關(guān)鍵點(diǎn)了:閾值是多大?
還是看回代碼,HotSpot 20:
C++代碼
void set_object_alignment() { assert(is_power_of_2(ObjectAlignmentInBytes), "ObjectAlignmentInBytes must be power of 2"); MinObjAlignmentInBytes = ObjectAlignmentInBytes; assert(MinObjAlignmentInBytes >= HeapWordsPerLong * HeapWordSize,
"ObjectAlignmentInBytes value is too small"); MinObjAlignment = MinObjAlignmentInBytes / HeapWordSize; assert(MinObjAlignmentInBytes == MinObjAlignment * HeapWordSize,
"ObjectAlignmentInBytes value is incorrect"); MinObjAlignmentInBytesMask = MinObjAlignmentInBytes - 1; LogMinObjAlignmentInBytes = exact_log2(ObjectAlignmentInBytes); LogMinObjAlignment = LogMinObjAlignmentInBytes - LogHeapWordSize; OopEncodingHeapMax = (uint64_t(max_juint) + 1) << LogMinObjAlignmentInBytes; } inline uintx max_heap_for_compressed_oops() { if (OopEncodingHeapMax < MaxPermSize + os::vm_page_size()) { return 0; } LP64_ONLY(return OopEncodingHeapMax - MaxPermSize - os::vm_page_size()); NOT_LP64(ShouldNotReachHere(); return 0); }
(注:其中 (uint64_t(max_juint) + 1) 的值也被稱為NarrowOopHeapMax,也就是2的32次方,0x100000000;
ObjectAlignmentInBytes在64位HotSpot上默認(rèn)為8;
HeapWord在globalDefinitions.hpp里定義,大小跟一個(gè)char*一樣;
HeapWordSize在同一個(gè)文件里定義,等于sizeof(HeapWord),在64位系統(tǒng)上值為8;
LogHeapWordSize也在同一文件里,在64位系統(tǒng)上定義為3)
跟蹤一下里面幾個(gè)參數(shù)的計(jì)算,在64位HotSpot上有,
C++代碼
- ObjectAlignmentInBytes = 8
- MinObjAlignmentInBytes = 8
- HeapWordSize = 8
- MinObjAlignment = 1
- MinObjAlignmentInBytesMask = 0x0111
- LogMinObjAlignmentInBytes = 3
- LogHeapWordSize = 3
- LogMinObjAlignment = 0
OopEncodingHeapMax = 0x800000000
于是,前面提到的第2個(gè)條件在64位HotSpot VM上默認(rèn)是:
C++代碼
MaxHeapSize + MaxPermSize + os::vm_page_size() <= 32GB
os::vm_page_size()是操作系統(tǒng)的虛擬內(nèi)存的分頁大小,在Linux上等于sysconf(_SC_PAGESIZE)的值;在x86_64上的Linux默認(rèn)分頁大小為4KB.
MaxHeapSize的值基本上等于-Xmx參數(shù)設(shè)置的值(會(huì)根據(jù)分頁大小、對(duì)齊等因素做調(diào)整)。
MaxPermSize就是perm gen設(shè)置的最大大小。
這下可以確認(rèn),在我現(xiàn)在用的環(huán)境里,當(dāng)包括perm gen在內(nèi)的GC堆大小在32GB - 4KB以下的時(shí)候,使用64位的JDK 6 update 23或更高版本就會(huì)自動(dòng)開啟UseCompressedOops功能