我将尝试对Alexey提供的答案进行一点延伸,因为有些事情可能并不明显.
根据Alexey的建议,如果我们在OpenJDK的源代码中搜索分配了压缩klass位移值的位置,我们将在metaspace.cpp中找到以下代码:
void Metaspace::set_narrow_klass_base_and_shift(address metaspace_base, address cds_base) {
// some code removed
if ((uint64_t)(higher_address - lower_base) <= UnscaledClassSpaceMax) {
Universe::set_narrow_klass_shift(0);
} else {
assert(!UseSharedSpaces, "Cannot shift with UseSharedSpaces");
Universe::set_narrow_klass_shift(LogKlassAlignmentInBytes);
}
我们可以看到,类移位可以是0(或基本上没有移位)或3位,因为LogKlassAlignmentInBytes是globalDefinitions.hpp中定义的常量:
const int LogKlassAlignmentInBytes = 3;
所以,你的排名的答案:
I was really expecting to both be “4-bit shift”. Why they are not?
是ObjectAlignmentInBytes对元空间中的压缩类指针对齐没有任何影响,它始终是8bytes.
当然,这个结论并没有回答这个问题:
“为什么在使用-XX:ObjectAlignmentInBytes = 16和-XX:UseCompressedClassPointers时,狭窄的klass移位变为零?另外,如果堆是4GB或更多,那么如果没有移动JVM如何用32位引用引用类空间?”
我们已经知道类空间是在java堆之上分配的,并且可以达到3G的大小.考虑到这一点,让我们做一些测试. -XX:UseCompressedOops -XX:默认情况下启用UseCompressedClassPointers,因此我们可以消除这些以简洁.
测试1:默认值 – 8个字节对齐
$java -XX:ObjectAlignmentInBytes=8 -XX:+UnlockDiagnosticVMOptions -XX:+PrintCompressedOopsMode -version
heap address: 0x00000006c0000000, size: 4096 MB, zero based Compressed Oops
Narrow klass base: 0x0000000000000000, Narrow klass shift: 3
Compressed class space size: 1073741824 Address: 0x00000007c0000000 Req Addr: 0x00000007c0000000
请注意,堆从虚拟空间中的地址0x00000006c0000000开始,大小为4GBytes.让我们从堆开始的位置跳过4Gbytes,然后我们就到了类空间开始的地方.
0x00000006c0000000 + 0x0000000100000000 = 0x00000007c0000000
类空间大小为1Gbyte,所以让我们跳过另一个1Gbyte:
0x00000007c0000000 + 0x0000000040000000 = 0x000000
I was really expecting to both be "4-bit shift". Why they are not?00000
我们降到32Gbytes以下.通过3位类空间移位,JVM能够引用整个类空间,尽管它处于极限(有意).
测试2:16字节对齐
java -XX:ObjectAlignmentInBytes=16 -XX:+UnlockDiagnosticVMOptions -XX:+PrintCompressedOopsMode -version
heap address: 0x0000000f00000000, size: 4096 MB, zero based Compressed Oops
Narrow klass base: 0x0000001000000000, Narrow klass shift: 0
Compressed class space size: 1073741824 Address: 0x0000001000000000 Req Addr: 0x0000001000000000
这次我们可以观察到堆地址不同,但让我们尝试相同的步骤:
0x0000000f00000000 + 0x0000000100000000 = 0x0000001000000000
这次堆空间在64GBytes虚拟空间边界下结束,类空间分配在64Gbyte边界以上.由于类空间只能使用3位移位,JVM如何引用位于64Gbyte以上的类空间?关键是:
Narrow klass base: 0x0000001000000000
JVM仍然使用32位压缩指针作为类空间,但是当对它们进行编码和解码时,它总是将0x0000001000000000 base添加到压缩引用而不是使用移位.请注意,只要引用的内存块低于4Gbytes(32位引用的限制),此方法就可以工作.考虑到类空间可以具有最大3G字节,我们可以轻松地在限制范围内.
3:16字节对齐,引脚堆基址为8g
$java -XX:ObjectAlignmentInBytes=16 -XX:HeapBaseMinAddress=8g -XX:+UnlockDiagnosticVMOptions -XX:+PrintCompressedOopsMode -version
heap address: 0x0000000200000000, size: 4096 MB, zero based Compressed Oops
Narrow klass base: 0x0000000000000000, Narrow klass shift: 3
Compressed class space size: 1073741824 Address: 0x0000000300000000 Req Addr: 0x0000000300000000
在此测试中,我们仍然保留-XX:ObjectAlignmentInBytes = 16,但还要求JVM使用-XX:HeapBaseMinAddress = 8g JVM参数在虚拟地址空间的第8 GByte分配堆.类空间将在虚拟地址空间中以第12 GByte开始,并且3位移位足以引用它.
希望这些测试及其结果能够回答这个问题:
“Why when using -XX:ObjectAlignmentInBytes=16 with -XX:+UseCompressedClassPointers the narrow klass shift becomes zero? Also, without shifting how can the JVM reference the class space with 32-bit references, if the heap is 4GBytes or more?”