目录
一、对象的创建
上一篇讲到了JVM内存模型,其中堆就是对象创建所在的区域。在上层语言编写层面,创建对象就是一个简单的new关键字,但是在虚拟机中,具体的过程时怎样的呢?
在上述流程中,在为新对象分配内存的过程中,分配方式有2中:
- 指针碰撞
假设堆中的内存是规整的,用过的内存在一边,没有使用的内存在另一边,而中间有一个指针作为分界点,而内存分配则就是将中间的指针由使用过的内存方往未使用过的内存方向移动一段对象大小的距离。
- 空闲列表
假设堆中的内存是不规整的。使用过的内存和未使用过的内存是交错的。虚拟机维护一个列表,记录着哪些内存块是可用的,分配的时候从可用的列表中挑选一个大小合适的内存块给对象使用。
选择那种分配方式由堆内存是否规整决定,而堆内存是否规整由具体的垃圾收集器及其收集算法决定。
内存分配完成以后,虚拟机将分配到的内存空间除对象头以外都初始化为零值,这也就说明了为什么Java代码中对象的实例字段在创建完对象以后可以不赋初始值就直接使用。
二、内存布局
对象在内存中的存储布局可以分为3块区域:对象头、实例数据和对齐填充。
- 对象头
包括两部分:
一部分存储对象运行时的数据,如哈希码,锁信息,GC分代信息;
另一部分是类型指针,即对象指向它的类元数据的指针。
- 实例数据
代码中定义的各种类型的字段的信息。
- 对其填充
没有特别的含义,仅起到占位符的作用。
三、对象访问
像下面一段代码,创建一个对象object:
Object object = new Object();
变量object是存在虚拟机栈上,创建出来的对象会存储在堆中,栈上的变量通过引用来和堆中的实际对象进行关联。
至于引用的方式,有直接指针和使用句柄两种方式。
两种方式的区别就是直接指针的方式是通过指针将变量和对象关联,而句柄池则是在变量关联对象的句柄地址,句柄地址包含了对象的实际地址。
如下图:
通过句柄访问:
直接指针访问:
两种方式的优缺点:
直接指针方式:
- 访问的速度快;
句柄池方式:
- 变量关联的句柄地址是固定的;对象地址改变只会修改句柄中实例数据指针,并不会改变句柄的地址;
- 访问对象实际上调了2次引用;