
jvm 遇到new指令,检查能否在常量池中定位到一个类的符号引用,检查这个符号引用代表的类是否已经经历类加载的过程,没有就执行类加载。
类加载完成 可以确定对象所需内存大小,分配方式有碰撞指针和空闲列表两种方式
内存分配完成后,jvm将除对象头之外的内存空间都初始化为零值,保证对象的字段可以不初始化直接使用。(对使用TLAB也一样)
设置对象头中的信息
执行 — 调用构造方法进行初始化
将对象指向引用变量(如果是new Object()就没有这一步)
对于Object obj = new Object()

此处使用jclasslib插件查看字节码,安装好后在View中,选择Show Bytecode With Jclasslib



在idea中查看一个对象在内存中的存储信息,依赖jol包:java object layout
注意依赖的版本和使用的JDK版本对应
<dependency>
<groupId>org.openjdk.jol</groupId>
<artifactId>jol-core</artifactId>
<version>0.8</version>
</dependency>
public class Test2 {
private static class User{
}
public static void main(String[] args) throws InterruptedException {
User user = new User();
// 解析实例 转为可打印的
System.out.println(ClassLayout.parseInstance(user).toPrintable());
}
}
结果:

public class Test2 {
private static class User{
String name;
int age;
}
public static void main(String[] args) throws InterruptedException {
User user = new User();
// 解析实例 转为可打印的
System.out.println(ClassLayout.parseInstance(user).toPrintable());
}
}
结果:

这里String类型的age变量占4字节
引用类型的变量默认开启压缩,压缩后是4字节
public class Test2 {
private static class User{
String name;
int age;
}
public static void main(String[] args) {
User[] users = new User[10];
System.out.println(ClassLayout.parseInstance(users).toPrintable());
}
}

new 一个长度为10的数组,一个元素的引用占4字节,所有元素引用一共占40字节
Mark Word=8字节,Class Pointer=4字节,Array Length=4字节
8+4+4+40=56字节
public static void main(String[] args) {
User user = new User();
//作为锁前:打印对象信息
System.out.println(ClassLayout.parseInstance(user).toPrintable());
//对象作为锁时,打印对象信息
synchronized(user){
System.out.println(ClassLayout.parseInstance(user).toPrintable());
}
//释放锁后:打印对象信息
System.out.println(ClassLayout.parseInstance(user).toPrintable());
}
结果:

在使用synchronized锁住一个对象时,这个对象的mark word部分会作一个记录
通过栈上的reference来操作堆上的对象
栈上的引用变量,是由JVM来实现访问对象的方式的
对象移动时,只改变句柄中的实例数据指针,ref本身不用改变节省了一次指针开销,速度更快CAS+失败重试来保证更新操作的原子性

对象所占字节=Mark Word + Class Pointer + Instance Data + Padding