Java是一门面向对象的语言,在Java程序运行的过程中无时无刻都有对象被创建出来。
那么在JVM虚拟机中,对象创建到底是怎么样一个过程呢?
1、虚拟机遇到一条new指令时,首先检查这个对应的类能否在常量池中定位到一个类的符号引用
2、判断这个类是否已被加载、解析和初始化
如果没有,则必须进行相应的加载过程
3、为新生对象在Java堆中分配内存空间
分配内存有两种方式:指针碰撞和空闲列表
指针碰撞是指:假设Java堆中内存绝对规整,所有已使用的内存都放在一边,空闲的内存都放在另一边,中间放着一个指针作为分界点,那这时候分配内存就仅仅是把这个指针向空闲的那边挪动一段(挪动的大小就是需要分配的对象的大小),这种分配方式就称为“指针碰撞”。
空闲列表是指:如果Java堆内存并不是规整的,已经使用的内存和空闲的内存相互交错,那肯定就没办法使用指针碰撞的方式进行内存分配了,这时候虚拟机就必须维护一个列表来记录哪些内存块儿是可用的,然后在需要进行内存分配的时候就从列表中找到一块儿足够大的内存划分给对象,并且更新列表上的记录,这种分配方式称之为“空闲列表”。
到底用哪种方式进行分配是由堆内存是否规整决定的,而堆内存是否规整又是由你具体使用的哪种垃圾回收器决定的,
如果你使用的是“标记-清除”这种类型的垃圾回收器,那么会导致堆内存不规则产生内存碎片,适合使用空闲列表的方式;
如果你使用的是“标记-整理(压缩)”这种类型的垃圾回收器,适合使用指针碰撞的方式。
除了讨论使用哪种方式进行内存分配外,还有一个问题需要考虑:对象创建在虚拟机中是非常频繁的行为,即使是仅仅修改一个指针指向的位置(指针碰撞的方式)或者找到空闲空间给对象分配,并更新列表(空闲列表的方式),在并发情况下也并不是安全的,因为上述的操作并不能保证其原子性,很可能出现不同的对象申请到同一块内存的情况
解决这个问题有几种方案:基于硬件指令的CAS方式来保证操作的原子性或者使用TLAB的方式,再或者可以使用栈上分配
栈上分配和TLAB的具体内容本篇不展开讨论,具体内容可以参考另一篇:对象的栈上分配和TLAB
4、内存分配完之后,虚拟机需要将分配到的内存空间都初始化为零值
比如int 型的零值是0,布尔的零值是false,引用数据类型零值是null等等
5、设置对象头相关数据
- GC分代年龄
- 对象的哈希码 hashCode
- 元数据信息
- 等等
6、执行<init>方法
执行<init>方法包括但不仅限于:静态代码块儿、构造代码块儿、构造函数
好啦,上述就是针对对象创建底层步骤的一些总结,希望对大家有所帮助
如有不对请指正,谢谢
对象在内存里创建出来后,那创建完的对象在内存中是什么样的?对象里都包括哪些内容?
下篇将继续讲一下Java对象在内存中的布局