java语言中我们通过new关键字来创建对象,而这仅仅是语言层面的创建对象,对象创建的原理是怎么样的呢?
我们从jvm的角度分析一下对象创建的过程。
对象的创建大概分为以下几步:
1:通过new关键字定义一个引用符号
2:JVM首先对符号引用进行解析检查类是否已经被加载;
3:为对象分配内存空间;
4:为对象字段设置零值;
5:设置对象头;
6:执行构造方法;
1. 通过new关键字定义一个引用符号
通过new关键字定义一个引用符号,这个符号引用时判断类是否被加载的关键
2.引用符号存放在运行时常量池中,JVM首先对符号引用进行解析检查类是否已经被加载;如果没有这个符号引用就说明这个类没有被加载到,进行类加载,否则下一步进行分配空间
3.为对象分配内存空间
类加载完后为对象分配空间,该空间的大小在类加载完成时就已经确定下来了
为对象分配内存空间有两种方式:
3.1.指针碰撞方式:如果Java堆中内存是规整排列的,所有被用过的内存放一边,空闲的可用内存放一边,中间放置一个指针作为它们的分界点,在需要为新生对象分配内存的时候,只要将指针向空闲内存那边挪动一段与对象大小相等的距离即可分配。
3.2.2.空闲列表方式:如果Java堆中内存不是规整排列的,用过的内存和可用内存是相互交错的,这种情况下将不能使用指针碰撞方式分配内存,Java虚拟机需要维护一个列表用于记录哪些内存是可用的,在为新生对象分配内存的时候,在列表中寻找一块足够大的内存分配,并更新列表上的记录。弊端就是空间碎片化问题严重
选用哪种策略取决于选用哪种垃圾回收器
4.为对象字段设置零值
零值初始化意思就是对对象的字段赋0值,或者null值,这也就是为什么这些字段在不需要显示初始化时候就能直接使用;
而方法的局部变量却必须要显示初始化后才可以访问,因为对象字段(成员变量存放在堆中),而方法变量存放在栈中每个栈帧的局部变量表里面
5:设置对象头
虚拟机需要对这个将要创建出来的对象,进行信息标记,包括是否为新生代/老年代,对象的哈希码,元数据信息,这些标记存放在对象头信息中
对象的组成结构:
6:执行构造方法
这是最后一步,对象的初始化,执行对象的构造方法,这里做的操作才是程序员真正想做的操作,例如初始化对象的成员变量值等等操作,至此,对象创建成功。
对象创建完毕之后一般会有一个引用指向这个对象,在java中引用的实现方式(对象的访问定位)
1.句柄访问
如果使用句柄访问的话,Java堆中将可能会划分出一块内存来作为句柄池,reference中存储的就 是对象的句柄地址,而句柄中包含了对象实例数据与类型数据各自具体的地址信息
2.直接指针访问
如果使用直接指针访问的话,Java堆中对象的内存布局就必须考虑如何放置访问类型数据的相关 信息,reference中存储的直接就是对象地址
使用直接指针来访问最大的好处就是速度更快,相对于句柄访问它节省了一次指针定位的时间开销,由于对象访问在Java中非常频繁,因此这类开销积少成多也是一项极为可观的执行成本