在java中,对象的创建仅仅只是new一下而已,但是其实远远没有这么简单.
一. 检查是否类是否加载
当java虚拟机遇到一条new指令对应的字节码时候,首先将会去检查这个指令的参数能否在常量池中,获取到一个类的符号引用.并且检查这个符号引用代表的类是否被加载,解析和初始化过.如果没有,那么就先执行类的加载过程.
二. 分配内存
对象所需要的内存大小,在类加载之后就可以确定.所以就是从java堆内存中划分一块确定的内存出来.
如何分出来有两种方法.
1. 指针碰撞
假设堆内存中是规整连续的,使用过和没使用过的严格分开,这样只需要维护一个指针,分配内存之后,移动一下指针就可以了
2. 空闲列表
如果java堆内存并不规整,已使用和未占用杂乱放置,就无法使用简单碰撞
如下图所示,实际要复杂的多,不同区块的大小也不一样,当有新的对象需要分配内存时候,就从空闲列表中取出一块足够大的内存分配给它(能力有限,怎么简单怎么画了)
内存分配总结
一般带有压缩整理功能的(ParNew
),可以使用指针碰撞方法分配内存,基于标记-清除算法的(CMS
),一般使用空闲列表
HotSpot虚拟机有一些改进,在cms回收器的实现下,会先使用空闲列表,先拿到一些大块的内存,在这些大块的内存里面,又可以使用指针碰撞方法
如果只按照上面的分配内存的办法,是会产生线程安全问题,所以实际上,在分配内存的时候,还会加入cas
和失败重试
的机制
三. 将对象的属性初始化为零值
四. 设置对象头信息
对象头包括两类信息
-
存储对象自身的运行时数据
如哈希码(HashCode)、GC分代年龄、锁状态标志、线程持有的锁、偏向线程ID、偏向时间戳等
-
类型指针
即对象指向它的类型元数据的指针,Java虚拟机通过这个指针来确定该对象是哪个类的实例
-
如果是数组,还要包括数组的长度信息
五. 执行init方法,也就是java的构造函数
其实上面四步执行完,虚拟机的对象创建已经完成,后面的init方法即java中的构造函数,就是程序员按照自己意愿对此对象进行的初始化