如题。先前都没仔细留意,今天在看一个crash log的时候才发现这点,记录一下。
本来以为这个是在6 update 25才开始开启的…
[color=red]注意:32位HotSpot VM是不支持UseCompressedOops参数的,只有64位HotSpot VM才支持。64位HotSpot VM的版本字符串样子如下:[/color]
[code]Java HotSpot(TM) 64-Bit Server VM (build 19.0-b09, mixed mode)[/code]
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参数的处理的差异:
[url=http://hg.openjdk.java.net/hsx/hsx16/baseline/file/tip/src/share/vm/runtime/arguments.cpp]HotSpot 16[/url]:
#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
[url=http://hg.openjdk.java.net/hsx/hsx17/baseline/file/tip/src/share/vm/runtime/arguments.cpp]HotSpot 17[/url]:
#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
[url=http://hg.openjdk.java.net/hsx/hsx19/baseline/file/tip/src/share/vm/runtime/arguments.cpp]HotSpot 19[/url] / [url=http://hg.openjdk.java.net/hsx/hsx20/baseline/file/tip/src/share/vm/runtime/arguments.cpp]HotSpot 20[/url]:
#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的版本号之间的关系,请参考另一篇笔记:[url=https://gist.github.com/925323]Sun/Oracle JDK、OpenJDK、HotSpot VM版本之间的对应关系[/url])
可以看到,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。
(更新:G1 GC的限制在[url=http://bugs.sun.com/bugdatabase/view_bug.do?bug_id=7017008]7017008: G1: Turn on compressed oops by default.[/url]已经解除了,于HS21 checkin,也就是说JDK7开始就没有这个限制了。)
第1、3、4点都很直观,于是第2点就是个关键点了:阈值是多大?
还是看回代码,[url=http://hg.openjdk.java.net/hsx/hsx20/baseline/file/tip/src/share/vm/runtime/arguments.cpp]HotSpot 20[/url]:
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在[url=http://hg.openjdk.java.net/hsx/hsx20/baseline/file/tip/src/share/vm/utilities/globalDefinitions.hpp]globalDefinitions.hpp[/url]里定义,大小跟一个char*一样;
HeapWordSize在同一个文件里定义,等于sizeof(HeapWord),在64位系统上值为8;
LogHeapWordSize也在同一文件里,在64位系统上定义为3)
跟踪一下里面几个参数的计算,在64位HotSpot上有,
ObjectAlignmentInBytes = 8
MinObjAlignmentInBytes = 8
HeapWordSize = 8
MinObjAlignment = 1
MinObjAlignmentInBytesMask = 0x0111
LogMinObjAlignmentInBytes = 3
LogHeapWordSize = 3 // _LP64
LogMinObjAlignment = 0
OopEncodingHeapMax = 0x800000000 // 32GB
于是,前面提到的第2个条件在64位HotSpot VM上默认是:
MaxHeapSize + MaxPermSize + os::vm_page_size() <= 32GB
os::vm_page_size()是操作系统的虚拟内存的分页大小,在Linux上等于sysconf(_SC_PAGESIZE)的值;在x86_64上的Linux默认分页大小为4KB。
MaxHeapSize的值基本上等于-Xmx参数设置的值(会根据分页大小、对齐等因素做调整)。
MaxPermSize就是perm gen设置的最大大小。
[color=darkred]注意:通过显式设置-XX:ObjectAlignmentInBytes参数到大于8的值,UseCompressedOops的应用范围可以增加。例如说-XX:ObjectAlignmentInBytes=16能让压缩指针在64GB或以下的GC heap上开启。[/color]
这值的计算也曾经RP过:
[quote]Changeset: b7268662a986
Author: coleenp
Date: 2008-04-29 19:31 -0400
URL: http://hg.openjdk.java.net/jdk7/hotspot-comp/hotspot/rev/b7268662a986
6689523: max heap calculation for compressed oops is off by MaxPermSize
Summary: Need to subtract MaxPermSize from the total heap size when determining whether compressed oops is turned on.[/quote]
Oracle的[url=http://www.oracle.com/technetwork/java/javase/tech/vmoptions-jsp-140102.html]HotSpot VM参数文档[/url]里其实也有说UseCompressedOops的使用说明:
[quote]-XX:+UseCompressedOops
Enables the use of compressed pointers (object references represented as 32 bit offsets instead of 64-bit pointers) for optimized 64-bit performance with [color=red]Java heap sizes less than 32gb[/color].[/quote]
本文只是从源码层确认了该参数自动开启的条件。
这下可以确认,在我现在用的环境里,当包括perm gen在内的GC堆大小在32GB - 4KB以下的时候,使用64位的JDK 6 update 23或更高版本就会自动开启UseCompressedOops功能。
另外也记了一个gist来看默认情况下JDK6是否开启压缩指针:[url]https://gist.github.com/rednaxelafx/1333043[/url]
在VM启动的时候,可以给 [b]-XX:+UnlockDiagnosticVMOptions -XX:+PrintCompressedOopsMode[/b] 参数来确认压缩指针的工作模式。
例子请看:[url]https://gist.github.com/rednaxelafx/998125[/url]
HotSpot VM现在只使用3种模式的压缩指针:
1、当整个GC堆所预留的虚拟地址范围的最高的地址在4GB以下的时候,使用"zero based Compressed Oops, 32-bits Oops"模式,也就是基地址为0、shift也为0;
2、当GC堆的最高地址超过了4GB,但在32GB以下的时候,使用"zero based Compressed Oops"模式,也就是基地址为0、shift为 LogMinObjAlignmentInBytes (默认为3)的模式;
3、当GC堆的最高地址超过了32GB,但整个GC堆的大小仍然在32GB以下的时候,使用非零基地址、shift为 LogMinObjAlignmentInBytes (默认为3)的模式。
如果上面三种情况都无法满足,那压缩指针就无法使用了。
上述三种模式的名字在Universe类里有声明:
hotspot/src/share/vm/memory/universe.hpp
// Narrow Oop encoding mode:
// 0 - Use 32-bits oops without encoding when
// NarrowOopHeapBaseMin + heap_size < 4Gb
// 1 - Use zero based compressed oops with encoding when
// NarrowOopHeapBaseMin + heap_size < 32Gb
// 2 - Use compressed oops with heap base + encoding.
enum NARROW_OOP_MODE {
UnscaledNarrowOop = 0,
ZeroBasedNarrowOop = 1,
HeapBasedNarrowOop = 2
};
可见,HotSpot VM并不尝试尽量减小shift的数值,而是使用固定数值(与对象对齐相关)或者不做shift。
这点上IBM J9 VM则采取了不同的策略,总是会尝试使用最小的shift值。
==========================================
在64位HotSpot上,不开压缩指针的时候对象头的结构是:
+8: [ _klass (8 bytes) ]
+16: ...[/code]
开启压缩指针时:
[code="">+0: [ _mark (8 bytes) ]
+8: [ _narrow_klass (4 bytes) ]
+12: [ padding or first field (4 bytes) ]
+16: ...
==========================================
顺带记几个链接:
HotSpot早期的compressed oops的代码提交
[quote]6420645: Create a vm that uses compressed oops for up to 32gb heapsizes
Summary: Compressed oops in instances, arrays, and headers. Code contributors are coleenp, phh, never, swamyv
Reviewed-by: jmasa, kamg, acorn, tbell, kvn, rasbold[/quote]
[quote]6689060: Escape Analysis does not work with Compressed Oops
Summary: 64-bits VM crashes with -XX:+AggresiveOpts (Escape Analysis + Compressed Oops)
Reviewed-by: never, sgoldman[/quote]
[quote]6711701: disable compressed oops by default[/quote]