JDK從6 update 23開始在64位系統上會默認開啟壓縮指針
如題。先前都沒仔細留意,今天在看一個crash log的時候才發現這點,記錄一下。
本來以為這個是在6 update 25才開始開啟的…
Sun的HotSpot VM從JDK5開始會根據運行環境來自動設定VM的一些參數(ergonomics)。其中大家最熟悉的可能是它會自動選擇client與server模式、堆的初始和***大小等。事實上ergonomics會設置非常多的內部參數,包括自動選擇GC算法、并行GC的線程數、GC的工作區分塊大小、對象晉升閾值等等。
Ergonomics相關的邏輯大都在hotspot/src/share/vm/runtime/arguments.cpp中,值得留意的是使用了FLAG_SET_ERGO()的地方。
于是我們可以留意一下幾個版本的HotSpot對UseCompressedOops參數的處理的差異:
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的版本號之間的關系,請參考另一篇筆記:Sun/Oracle JDK、OpenJDK、HotSpot VM版本之間的對應關系)
可以看到,UseCompressedOops參數從HotSpot 19開始終于開始受ergonomics控制,會在下述條件滿足的時候默認開啟:
1、是64位系統(#ifdef _LP64)并且不是client VM(#ifndef COMPILER1);
2、Java堆的***大小不大于一個閾值(MaxHeapSize <= max_heap_for_compressed_oops());
3、沒有通過.hotspotrc或命令行參數手動設定過UseCompressedOops參數的值;
4、沒有使用Garbage-First (G1) GC。
第1、3、4點都很直觀,于是第2點就是個關鍵點了:閾值是多大?
還是看回代碼,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位系統上值為8;
LogHeapWordSize也在同一文件里,在64位系統上定義為3)
跟蹤一下里面幾個參數的計算,在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()是操作系統的虛擬內存的分頁大小,在Linux上等于sysconf(_SC_PAGESIZE)的值;在x86_64上的Linux默認分頁大小為4KB。
MaxHeapSize的值基本上等于-Xmx參數設置的值(會根據分頁大小、對齊等因素做調整)。
MaxPermSize就是perm gen設置的***大小。
這下可以確認,在我現在用的環境里,當包括perm gen在內的GC堆大小在32GB - 4KB以下的時候,使用64位的JDK 6 update 23或更高版本就會自動開啟UseCompressedOops功能。
【編輯推薦】