[讨论] [HotSpot VM] 关于hsdis与64位Java实例的内存结构问题
Ferdiknight
2014-11-18
今天用HSDB的时候看到instance的内存值有些困惑,写了个Demo试了下:
public class Demo { private final long L = 0x7EFFFFFFFFL; private final int I = 0x7FFFFF; private final int J = 0x7EFFFF; } Class Brower看到的field偏移量如下: private final long L; (offset = 16) private final int I; (offset = 12) private final int J; (offset = 24) 找到Demo的地址 0x00000007d569f1c8 mem 0x00000007d569f1c8 4 得到如下结果: 0x00000007d569f1c8: 0x0000000000000001 0x00000007d569f1d0: 0x007fffffef650cc4 0x00000007d569f1d8: 0x0000007effffffff 0x00000007d569f1e0: 0x00000000007effff 第一个8字节 是_mark:1,不过mark不是应该是4字节吗? 第二个8字节 前4字节为 I,这个I应该是开启了压缩的结果填充上来的,可是为啥没有填充在_mark的后4字节呢? 后4字节是klassoop? 但是Demo的InstanceKlass 是在0x000000077b286620; 第三个8个字节是L,这里比较困惑的是,如果第二个8字节自己的offset12是在前面的话,为啥这里的顺序没有倒过来呢…… 第四个8个字节没啥问题,就是offset 24加上 4字节填充; 是HSDB的显示问题吗,如果是long型会顺序的显示8个字节?这样的话是不是说明_mark也有可能是8字节啊,所以I没有填充到第一个空隙的4字节,因为其实没有空隙? VM是 hotspot 64-bit server 24.72-b04,对应jdk是1.7.0u72,系统是windows 7 64bit。 然后问下 hotspot对运行时实例的内存结构定义哪些资料会有比较系统的介绍呢? 搭车问下jdk1.7.0ux 64bit在windows下面用hsdis的问题,已经分别用过kenai的basic-hsdis和jdk7dev的hsdis编译出相应的dll,但是放到jvm.dll一起都没效果,而且像32位的会报错,显示无法加载dll,但是64的完全没有报错信息,也不打印任何输出,求有经验的指点一二~ 坐等R大~~ |
|
RednaxelaFX
2014-11-19
就楼主使用的JDK7u72的HotSpot VM来说。
0. 楼主的例子肯定开了压缩指针(多半没设-Xmx或者是-Xmx比较小所以压缩指针自动开了)。 1.无论开不开UseCompressedOops, 64位HotSpot VM的mark word都是8字节。 2. 接下来如果开UseCompressedOops的话,_compressed_klass占4字节;反之则_klass占8字节。 3. _compressed_klass要转换回到正常oop需要做一定运算,具体是什么运算取决于当前的压缩模式。参考这帖:http://rednaxelafx.iteye.com/blog/1010079 4. 开了压缩指针的话oopDesc这个对象头只需要8+4=12字节,而long/double必须在8字节对齐的地址上分配,所以中间有4字节的空隙(“gap”)。 5. 这个gap可以尽可能的填充1个int/float,或者最多2个short/char,或者最多4个byte/boolean。 _mark: 0x00000007d569f1c8: 0x0000000000000001 _compressed_klass: 0x00000007d569f1d0: 0xef650cc4 I: 0x00000007d569f1d4: 0x007fffff L: 0x00000007d569f1d8: 0x0000007effffffff J: 0x00000007d569f1e0: 0x007effff (padding): 0x00000007d569f1e4: 0x00000000 问题解决? |
|
Ferdiknight
2014-11-19
RednaxelaFX 写道 就楼主使用的JDK7u72的HotSpot VM来说。
0. 楼主的例子肯定开了压缩指针(多半没设-Xmx或者是-Xmx比较小所以压缩指针自动开了)。 1.无论开不开UseCompressedOops, 64位HotSpot VM的mark word都是8字节。 2. 接下来如果开UseCompressedOops的话,_compressed_klass占4字节;反之则_klass占8字节。 3. _compressed_klass要转换回到正常oop需要做一定运算,具体是什么运算取决于当前的压缩模式。参考这帖:http://rednaxelafx.iteye.com/blog/1010079 4. 开了压缩指针的话oopDesc这个对象头只需要8+4=12字节,而long/double必须在8字节对齐的地址上分配,所以中间有4字节的空隙(“gap”)。 5. 这个gap可以尽可能的填充1个int/float,或者最多2个short/char,或者最多4个byte/boolean。 _mark: 0x00000007d569f1c8: 0x0000000000000001 _compressed_klass: 0x00000007d569f1d0: 0xef650cc4 I: 0x00000007d569f1d4: 0x007fffff L: 0x00000007d569f1d8: 0x0000007effffffff J: 0x00000007d569f1e0: 0x007effff (padding): 0x00000007d569f1e4: 0x00000000 问题解决? 嗯,明白~~ 感觉最大的不自然的地方是HSDB显示内存内容的顺序: 比如: 0x00000007d569f1d0: 0xef650cc4 0x00000007d569f1d4: 0x007fffff HSDB的输出是: 0x00000007d569f1d0: 0x007fffffef650cc4 下意识的就理解成了 0x00000007d569f1d0: 0x007fffff 0x00000007d569f1d4: 0xef650cc4这样的排列顺序,而且能在后面L的输出上得到证实……所以当时觉得很困惑,这个是HSDB在输出的时候的什么考虑吗? |
|
Ferdiknight
2014-11-19
信息回填以备后用
顺着R大解释压缩指针的博文,问了一些菊苣,找了些资料,下面是oracle的wiki: https://wikis.oracle.com/display/HotSpotInternals/CompressedOops 结合wiki,验证本帖的内容,压缩后的narrow_oop 是 0xef650cc4 hsdb查询到的wide_oop 是 0x000000077b286620 验证计算方式 0xef650cc4 << 3 = 0x000000077b286620 这只是其中一种模式,具体还跟压缩指针的模式相关,具体详见R大的回复中的博文以及上述oracle wiki. |
|
RednaxelaFX
2014-11-20
Ferdiknight 写道 嗯,明白~~ 感觉最大的不自然的地方是HSDB显示内存内容的顺序:
比如: 0x00000007d569f1d0: 0xef650cc4 0x00000007d569f1d4: 0x007fffff HSDB的输出是: 0x00000007d569f1d0: 0x007fffffef650cc4 下意识的就理解成了 0x00000007d569f1d0: 0x007fffff 0x00000007d569f1d4: 0xef650cc4这样的排列顺序,而且能在后面L的输出上得到证实……所以当时觉得很困惑,这个是HSDB在输出的时候的什么考虑吗? 楼主先看看这个解不解决问题:http://rednaxelafx.iteye.com/blog/257760 HSDB默认按8字节(64位)粒度来显示内存的内容。x86是little-endian架构。 |
|
Ferdiknight
2014-11-20
RednaxelaFX 写道 Ferdiknight 写道 嗯,明白~~ 感觉最大的不自然的地方是HSDB显示内存内容的顺序:
比如: 0x00000007d569f1d0: 0xef650cc4 0x00000007d569f1d4: 0x007fffff HSDB的输出是: 0x00000007d569f1d0: 0x007fffffef650cc4 下意识的就理解成了 0x00000007d569f1d0: 0x007fffff 0x00000007d569f1d4: 0xef650cc4这样的排列顺序,而且能在后面L的输出上得到证实……所以当时觉得很困惑,这个是HSDB在输出的时候的什么考虑吗? 楼主先看看这个解不解决问题:http://rednaxelafx.iteye.com/blog/257760 HSDB默认按8字节(64位)粒度来显示内存的内容。x86是little-endian架构。 谢谢R大,明白了 little-endian架构,所以其实在内存中实际的情况是这样: 0x00000007d569f1d0 : c4 0c 65 ef ff ff 7f 00 java在加载实例的时候是按照4字节来加载klass-oop和后面的gap的,所以能够正确得到: klass-oop : ef 65 0c c4 gap : 00 7f ff ff 而HSDB默认按照8字节输出内存内容,所以是: 00 7f ff ff ef 65 0c c4 之所以表现不同,是因为little-endian架构下 将内存内容加载到寄存器会反序,而反序的范围取决于加载的字节长度 |