1.HotSpot中,对象在内存中分为3块区域:
- 对象头
- MarkWord(存储hashCode,gc分代年龄,锁标志,线程id等)
- KlassPointer (class相当于是对klass做了个镜像,方便我们访问)
- 数组长度(如果是数组才有, 固定占4字节,此处也可看出数组长度length的最大值为 2147483647 ,即二进制31个1)
- 实例数据(成员变量等)
- 对齐填充(保证对象是8个字节的整倍数)
补充说明KlassPointer : 例如我们执行main方法,类加载器会将主类加载到JVM,经历 加载-》验证-》准备-》解析-》初始化的过程,其中加载这一步,会去生成 Class对象作为方法区中,这个类的各种数据的访问入口。(类元信息放方法区,class是堆中的)而jvm实际是通过方法区的klass得到这个类的信息。
32位:
64位:
2.通过一个demo了解这三块区域
- 引入依赖
<dependency>
<groupId>org.openjdk.jol</groupId>
<artifactId>jol-core</artifactId>
<version>0.9</version>
</dependency>
public class JOLSample {
public static void main(String[] args) {
ClassLayout layout = ClassLayout.parseInstance(new Object());
System.out.println(layout.toPrintable());
System.out.println();
ClassLayout layout1 = ClassLayout.parseInstance(new int[]{});
System.out.println(layout1.toPrintable());
System.out.println();
ClassLayout layout2 = ClassLayout.parseInstance(new A());
System.out.println(layout2.toPrintable());
}
// -XX:+UseCompressedOops 默认开启的压缩所有指针
// -XX:+UseCompressedClassPointers 默认开启的压缩对象头里的类型指针Klass Pointer
// Oops : Ordinary Object Pointers
public static class A {
//8byte mark word
//4byte Klass Pointer 如果关闭压缩-XX:-UseCompressedClassPointers或-XX:-UseCompressedOops,则占用8byte
int id; //4byte
String name; //4byte 如果关闭压缩-XX:-UseCompressedOops,则占用8byte
byte b; //1byte
Object o; //4byte 如果关闭压缩-XX:-UseCompressedOops,则占用8byte
}
}
- 结果说明
- OFFSET:偏移量 (不关注)
- SIZE:占用的字节
- TYPE:类型
- DESCRIPTION:前半部分为十六进制,后半部分为二进制(括号部分)
- window和linux默认为小端模式(更适合计算机底层原理,即从低位读取)
00001010 00001011 00001111 10000000 //工具打印出来的
10000000 00001111 00001011 00001010 //实际上计算机中为反过来,此时锁为重量级锁
- window和linux默认为小端模式(更适合计算机底层原理,即从低位读取)
- Instance size: 总共占用的字节数
MarkWord用红框表示,klass指针用橙框,数组长度用灰框。
实例数据用黄框,对齐填充用淡蓝框
成员变量中的类大多都是占用4bit(指针压缩后4bit,未压缩8bit)
结果一:(没有成员变量的)
红色部分描述不严谨,实际为对象头的MarkWord部分
结果二:(数组)
int [I.element 部分为数组中的数据,demo中为空,所以占0字节,索引每多1个,多加4字节(加多少具体看类型,int为4)
结果三:(创建的普通类)
实例数据中, int占4byte;String占4byte(字符串是存储在常量池,所以此处占用的是name的引用指向池的‘地址’); byte和bool占1byte;long占8byte…
3.通过一个demo了解锁标志位
//TimeUnit.SECONDS.sleep(5);
Object o = new Object();
System.out.println(ClassLayout.parseInstance(o).toPrintable());
synchronized (o){
System.out.println(ClassLayout.parseInstance(o).toPrintable());
}
结果一:
00000001
00000000 00000000 00000000 (无锁状态)
结果二:
01101000
11110001 11110101 00000000(轻量级锁)
对应最上面的锁标志位可知,结果一为【无锁状态】,结果二为【轻量级锁】