简述::
1.确认类元信息是否存在,当 JVM 接收到 new 指令时,首先在 metaspace 内检查需要创建的类元信息是否存在。 若不存在,那么在双亲委派模式下,使用当前类加载器以 ClassLoader + 包名+类名为 Key 进行查找对应的 class 文件。 如果没有找到文件,则抛出 ClassNotFoundException 异常 , 如果找到,则进行类加载(加载 - 验证 - 准备 - 解析 - 初始化),并生成对应的 Class 类对象。
2.分配对象内存。 首先计算对象占用空间大小,如果实例成员变量是引用变量,仅分配引用变量空间即可,即 4 个字节大小,接着在堆中划分—块内存给新对象。 在分配内存空间时,需要进行同步操作,比如采用 CAS (Compare And Swap) 失败重试、 区域加锁等方式保证分配操作的原子性。
3.设定默认值。 成员变量值都需要设定为默认值, 即各种不同形式的零值。
4.设置对象头。设置新对象的哈希码、 GC 信息、锁信息、对象所属的类元信息等。这个过程的具体设置方式取决于 JVM 实现。
5.执行 init 方法。 初始化成员变量,执行实例化代码块,调用类的构造方法,并把堆内对象的首地址赋值给引用变量。
在Java中创建一个Object对象会占用多少内存?
对象头大小应该理论上为mrakword+klassword=16bytes=128bit,同时Object类中是没有定义任何属性的,所以不存在实例数据。但如果在开启指针压缩的情况下,只会有12bytes,因为对象头中的类元指针会被压缩一半,所以会出现4bytes的对齐填充,最终不管是否开启了指针压缩,大小应该为16字节,接着来论证一下(环境:默认开启指针压缩的JDK1.8版本)
1.maven引入OpenJDK组织提供的工具
工具中提供了两个API:
GraphLayout.parseInstance(obj).toPrintable():查看对象外部信息:包括引用的对象
GraphLayout.parseInstance(obj).totalSize():查看对象占用空间总大小
<!-- https://mvnrepository.com/artifact/org.openjdk.jol/jol-core -->
<dependency>
<groupId>org.openjdk.jol</groupId>
<artifactId>jol-core</artifactId>
<version>0.9</version>
</dependency>
import org.openjdk.jol.info.ClassLayout;
public class jvmtest {
public static void main(String[] args) {
Object a=new int[9];
System.out.println(ClassLayout.parseInstance(a).toPrintable());
}
}
输出结果:
image.png
从结果中可以很明显的看到,0-12byte为对象头,12-16byte为对齐填充数据,最终大小为16bytes,与上述的推测无误,在开启指针压缩的环境下,会出现4bytes的对齐填充数据。
数组对象大小计算
image.png上述简单分析了Object对象的大小之后,我们再来看一个案例,如下:
public static void main(String[] args){
Object obj = new int[9];
System.out.println(ClassLayout.parseInstance(obj).toPrintable());
}
此时大小又为多少呢?因为该数组为int数组,而int类型的大小为32bit/4bytes,所以理论上它的大小为:(12bytes对象头+9*4=36bytes数组空间) = 48bytes,对吗?先看看运行结果:
从结果中可以看出最终大小为56bytes,实际的大小与前面的推断存在明显出入,为什么呢?这是因为目前的obj对象是一个数组对象,在前面分析对象头构成的时候曾分析过,如果一个对象是数组对象,那么它的对象头中也会使用4bytes存储数组的长度,所以此时的obj对象头大小为16bytes,其中12~16bytes用于存储数组的长度,再加上9个int类型的数组空间36bytes,大小为52bytes,因为52不为8的整数倍,所以JVM会为其补充4bytes的对齐填充数据,最终大小就成了上述运行结果中的56bytes。
网友评论