Java对象的创建过程
Java是一个面向对象编程语言,我们在编写代码时无时无刻不在创建对象,那么我们在源代码中new一个对象的操作,映射到JVM中是如何执行的呢。接下来我们将对此展开讨论。
1、Java虚拟机当遇到new指令时,首先会去JVM的方法区看这个对象所属的类是否已经被加载,如果这个类还没有被加载,则虚拟机会先去加载此类到方法区,以及将该类的符号引用加载到方法区的常量池中。
2、为新创建的对象分配内存,分配内存的大小,在该类被加载时就可以完全确定下来。
3、分配完内存后,JVM将对除去对象头外的该对象内存空间初始化为零值,这也是为什么我们创建的对象,即使没有手动给对象内的属性变量赋值,默认也会是零值或者null。
4、接下来虚拟机给对象头进行赋值,包括给对象头的运行时数据部分赋值,这部分包括哈希值,GC分代年龄,锁状态等;另外在给对象头的类型指针部分赋值,将该指针指向方法区中以及加载的该类上。
经过以上步骤后,在虚拟机层面上来看,这个对象已经创建完成了。
Java对象在内存中的分布
这部分我们主要来了解下我们创建的对象在虚拟机中占了多少内存,以及对象引用和对象实例在虚拟机栈和虚拟机堆中的分布情况。
要了解对象在虚拟机中占用内存大小,就要先了解一个对象在虚拟机内存中的组成结构。
Java对象(非Java数组对象)在虚拟机中包含如下三部分。
- 对象头:对象头包括两部分,第一部分是运行时数据部分,这部分在32bit虚拟机和64bit虚拟机中分别占用4字节和8字节。第二部分是数据类型引用部分,这部分在开启了指针压缩和没开启指针压缩(注:开启指针压缩可通过设置参数-XX:+UseCompressedOops)中分别占用4字节和8字节。
- 对象实例的数据部分:这部分就是你定义对象时,该对象包含的具体属性变量占用的内存大小。对于基本数据类型就是那四类八种就占对应的内存大小,而对于引用类型,则再开启了指针压缩和没有开启指针压缩中分别占4字节和8字节。
- 对齐填充:无论是32bit虚拟机还是64bit虚拟机都是8字节对齐,所以如果对象头+实例数据部分内存 % 8不为0,则需要对齐填充部分。填充部分内存都为0。
举个栗子(均为64bit JVM)
class TestObj1
{
private int i; //占用内存4字节
private String str; //开启指针压缩(未开启指针压缩)4字节(8字节)
private List<Integer> list; //开启指针压缩(未开启指针压缩)4字节(8字节)
private Map<String,String>map; //开启指针压缩(未开启指针压缩)4字节(8字节)
//to do something....
}
所以new TestObj1()占用内存在开启指针压缩(未开启指针压缩)情况下占用内存为(8+4)+(4+4+4+4)+(4)=32字节((8+8)+(4+8+8+8)+(4)=48字节)。
可以看出在64bit JVM中如果未开启指针压缩,则会浪费一定的内存。但由于开启指针压缩是由于在机器码中植入压缩与解压指令,也会给JVM增加一定的额外开销。