[讨论] [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架构下 将内存内容加载到寄存器会反序,而反序的范围取决于加载的字节长度
Global site tag (gtag.js) - Google Analytics