实验对象代码
public class MyTest {
private String name;
private Integer age;
private Long money;
private long high = 7L;
private char sex = 'a';
private boolean flag = true;
private int num = 0x12345678;
private long leg = 9L;
public int test(int a){
a = 3;
return a;
}
public static void main(String[] args){
int a = 0;
a = a + 1;
MyTest mytest = new MyTest();
mytest.money = 1000L;
for(;;){}
}
}
一、Java对象在内存中布局是什么样的?-XX:-UseCompressedOops 压缩指针以及数据类型对排列顺序是否影响?对齐填充填充的是哪里?
启动命令:
java -Djava.compiler=NONE -XX:-UseCompressedOops MyTest
实验类MyTest ;64位HotSpot虚拟机;关闭压缩指针情况下 -XX:-UseCompressedOops
对象头大小 = 8 + 8 = 16
对象实例数据大小 = 8(指针name) + 8 (指针age) + 8 (指针money) + 8 (long类型high)+ 2(char类型sex) + 1(boolean类型) + 4(int类型num) + 8 (long类型 leg)= 47
对象实际大小 = 16 + 47 + 1(填充) = 64
通过HSDB 查看

注:左边图1是对象Oop ;右图2是该对象在内存中的排列,左边一列是内存地址,右边是内存中存的值
可以看到 该对象一共占了64个字节
第一行 0x0000025c1f00b070: 0x0000000000000001 (这个对象头markword好像存的是线程id)
第二列 0x0000025c1f00b078: 0x0000025c7cbb0630 (对象Klass指针,指向对象Klass内存地址)
第三列 0x0000025c1f00b080: 0x0000000000000007 (long数据类型high 的值,占8个字节)
第四行 0x0000025c1f00b088: 0x0000000000000009 (long数据类型leg 的值,占8个字节)
第五行 0x0000025c1f00b090: 0x0001006112345678 (这八个字节存储了三部分数据,从右到左的4个字节 12345678存的是int类型num值;紧跟后的2个字节 61 是char类型sex存的值'a' ascill码 97;再跟后面一个字节01 是boolean类型flag 存的true;再后面一个字节是填充数据)
第六行 0x0000025c1f00b098: 0x0000000000000000 (指针name 从图三的偏移位置40可看出)
第七行 0x0000025c1f00b0a0: 0x0000000000000000(指针age 从图三的偏移位置48可看出)
第八行 0x0000025c1f00b0a8: 0x0000025c1f00b880 (最后一列是指针Money指向的Long对象内存地址)
实验结果:1. 对象排列和数据类型有关,基本类型会在引用类型前面
2. 对齐填充是在某个数据类型不足8字节而填充至8字节
3. 64位Hotspot虚拟机,klass未开启压缩指针占 8 字节
二、开启压缩指针
启动命令
java -Djava.compiler=NONE -XX:+UseCompressedOops MyTest
实验类MyTest ;64位HotSpot虚拟机;开启压缩指针情况下 -XX:-UseCompressedOops
对象头大小 = 8 + 4(Klass压缩为4字节) = 12
对象实例数据大小 = 4(指针name) + 4 (指针age) + 4 (指针money) + 8 (long类型high)+ 2(char类型sex) + 1(boolean类型) + 4(int类型num) + 8 (long类型 leg)= 35
对象实际大小 = 12 + 35 + 1(填充) = 48

可以看到这次与关闭压缩指针内存节省了24个字节,对象内部布局及顺序也发生了变化
第一行 0x000000076bbe02a8: 0x0000000000000001
第二列 0x000000076bbe02b0: 0x12345678f800c005 (分了两部分数据,对象Klass指针4个字节,指向对象Klass内存地址;另一个int类型数据num)
第三列 0x000000076bbe02b8: 0x0000000000000007 (long数据类型high 的值,占8个字节)
第四行 0x000000076bbe02c0: 0x0000000000000009 (long数据类型leg 的值,占8个字节)
第五行 0x000000076bbe02c8: 0x0000000000010061 (这8个字节分为3部分数据,char类型占两个字节,boolean占一个字节,填充一个字节,在跟着指针name的4个字节,根据偏移位置36可以看出)
第六行 0x000000076bbe02d0: 0xed77c12500000000 (这部分分为两部分,开头4字节为指针age, 后面紧跟4字节是指针Money指向的内存地址)
关于压缩指针,我们知道32位机器最大寻址空间是2^32次方,而内存按8位为一字节进行编址,则最大寻址空间就是4G; 同理可得64位机器寻址空间可达到 2^32 * 4 G,但是压缩成
4字节的指针按道理只能寻址4G的地址空间,它是怎么做到寻址最大达到32G空间的?
后面大概了解到,java对象按8字节进行对齐,则可将指针原本按一个字节寻址,现在扩展为8字节来寻址,即原本指针指向 0x100,0x101... ;压缩指针后,0x100, 0x108...
,同理,你也可以只用一位来寻址64位的机器,只不过就指向这一个地址罢了。
本文探讨了Java对象在内存中的布局细节,包括对象头、实例数据和填充的作用,并通过实验对比了压缩指针开关状态下的内存占用变化。

412

被折叠的 条评论
为什么被折叠?



