JDK從6 update 23開始在64位系統(tǒng)上會默認開啟壓縮指針
如題。先前都沒仔細留意,今天在看一個crash log的時候才發(fā)現(xiàn)這點,記錄一下。
本來以為這個是在6 update 25才開始開啟的…
Sun的HotSpot VM從JDK5開始會根據(jù)運行環(huán)境來自動設(shè)定VM的一些參數(shù)(ergonomics)。其中大家最熟悉的可能是它會自動選擇client與server模式、堆的初始和***大小等。事實上ergonomics會設(shè)置非常多的內(nèi)部參數(shù),包括自動選擇GC算法、并行GC的線程數(shù)、GC的工作區(qū)分塊大小、對象晉升閾值等等。
Ergonomics相關(guān)的邏輯大都在hotspot/src/share/vm/runtime/arguments.cpp中,值得留意的是使用了FLAG_SET_ERGO()的地方。
于是我們可以留意一下幾個版本的HotSpot對UseCompressedOops參數(shù)的處理的差異:
HotSpot 16:
C++代碼
- #ifdef _LP64
- // Check that UseCompressedOops can be set with the max heap size allocated
- // by ergonomics.
- if (MaxHeapSize <= max_heap_for_compressed_oops()) {
- if (FLAG_IS_DEFAULT(UseCompressedOops)) {
- // Turn off until bug is fixed.
- // the following line to return it to default status.
- // FLAG_SET_ERGO(bool, UseCompressedOops, true);
- }
- // ...
- }
- #endif // _LP64
HotSpot 17:
C++代碼
- #ifndef ZERO
- #ifdef _LP64
- // Check that UseCompressedOops can be set with the max heap size allocated
- // by ergonomics.
- if (MaxHeapSize <= max_heap_for_compressed_oops()) {
- #ifndef COMPILER1
- if (FLAG_IS_DEFAULT(UseCompressedOops) && !UseG1GC) {
- // Disable Compressed Oops by default. Uncomment next line to enable it.
- // FLAG_SET_ERGO(bool, UseCompressedOops, true);
- }
- }
- #endif
- // ...
- #endif // _LP64
- #endif // !ZERO
HotSpot 19 / HotSpot 20:
C++代碼
- #ifndef ZERO
- #ifdef _LP64
- // Check that UseCompressedOops can be set with the max heap size allocated
- // by ergonomics.
- 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的版本號與JDK的版本號之間的關(guān)系,請參考另一篇筆記:Sun/Oracle JDK、OpenJDK、HotSpot VM版本之間的對應(yīng)關(guān)系)
可以看到,UseCompressedOops參數(shù)從HotSpot 19開始終于開始受ergonomics控制,會在下述條件滿足的時候默認開啟:
1、是64位系統(tǒng)(#ifdef _LP64)并且不是client VM(#ifndef COMPILER1);
2、Java堆的***大小不大于一個閾值(MaxHeapSize <= max_heap_for_compressed_oops());
3、沒有通過.hotspotrc或命令行參數(shù)手動設(shè)定過UseCompressedOops參數(shù)的值;
4、沒有使用Garbage-First (G1) GC。
第1、3、4點都很直觀,于是第2點就是個關(guān)鍵點了:閾值是多大?
還是看回代碼,HotSpot 20:
C++代碼
- void set_object_alignment() {
- // 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;
- // Oop encoding heap max
- OopEncodingHeapMax = (uint64_t(max_juint) + 1) << LogMinObjAlignmentInBytes;
- }
- inline uintx max_heap_for_compressed_oops() {
- // Avoid sign flip.
- 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上默認為8;
HeapWord在globalDefinitions.hpp里定義,大小跟一個char*一樣;
HeapWordSize在同一個文件里定義,等于sizeof(HeapWord),在64位系統(tǒng)上值為8;
LogHeapWordSize也在同一文件里,在64位系統(tǒng)上定義為3)
跟蹤一下里面幾個參數(shù)的計算,在64位HotSpot上有,
C++代碼
- ObjectAlignmentInBytes = 8
- MinObjAlignmentInBytes = 8
- HeapWordSize = 8
- MinObjAlignment = 1
- MinObjAlignmentInBytesMask = 0x0111
- LogMinObjAlignmentInBytes = 3
- LogHeapWordSize = 3 // _LP64
- LogMinObjAlignment = 0
- OopEncodingHeapMax = 0x800000000 // 32GB
于是,前面提到的第2個條件在64位HotSpot VM上默認是:
C++代碼
- MaxHeapSize + MaxPermSize + os::vm_page_size() <= 32GB
os::vm_page_size()是操作系統(tǒng)的虛擬內(nèi)存的分頁大小,在Linux上等于sysconf(_SC_PAGESIZE)的值;在x86_64上的Linux默認分頁大小為4KB。
MaxHeapSize的值基本上等于-Xmx參數(shù)設(shè)置的值(會根據(jù)分頁大小、對齊等因素做調(diào)整)。
MaxPermSize就是perm gen設(shè)置的***大小。
這下可以確認,在我現(xiàn)在用的環(huán)境里,當(dāng)包括perm gen在內(nèi)的GC堆大小在32GB - 4KB以下的時候,使用64位的JDK 6 update 23或更高版本就會自動開啟UseCompressedOops功能。
【編輯推薦】