Java教程

java对象的创建过程

本文主要是介绍java对象的创建过程,对大家解决编程问题具有一定的参考价值,需要的程序猿们随着小编来一起学习吧!

#1. 对象的创建

揭秘对象创建的过程new Object()

1.1 检查

在创建对象之前,会检查该对象的类的符号引用能否在常量池中定位到,如果能定位到,再继续检查该符号引用代表的类是否已经被加载解析初始化,如果没有定位到,就需要执行相应类加载过程

1.2 分配

在上面的检查操作执行完之后,会对新生对象进行内存分配,分配的内存大小在类加载时已经确定(类加载知识应该会讲到咋确定的,我现在也不确定它是咋确定的),内存的具体分配操作有两种

1.2.1 具体分配操作一(指针碰撞)

指针碰撞的假设是,java堆内存是绝对规整的。将内存区域分为两侧,一侧是使用的内存区域,一侧是空闲(没有被使用)的内存区域,中间放着一个指针作为分界点的指示器
,分配内存空间就是将指针向空闲方向移动一段与新生对象所需内存大小相等的距离。

注意:在并发情况下指针的修改并不是线程安全的,有可能A对象分配内存时,还未修改指针,此时B对象再使用原来的指针来分配内存

解决方案

  • 一:使用CAS加上失败重试来保证指针修改的原子性。
  • 二:把内存的分配工作根据线程划分再不同的空间进行,即每个线程在预先分配一小块内存,称为本地线程分配缓冲(Thread Local Allocation Buffer)简称 TLAB,线程创建对象都在自己的本地线程分配缓冲区中进行分配,只有本地线程分配缓冲区中的内存用完了后,分配新的缓冲区时才需要同步锁定,通过-XX:+/-UseTLAB参数决定是否使用本地线程分配缓冲区

1.2.2 具体分配操作二(空闲列表)

如果java堆内存不是规整的,被使用和空闲的内存交织在一起,就无法使用碰撞指针的方式分配内存,虚拟机就需要维护一个列表记录哪块内存是可用的,分配时从列表中找到足够大的内存空间,并更新列表上的记录。

1.2.3 两种分配操作的使用场景

选择哪种分配方式由java堆内存是否规整决定的,而java堆空间是否规整又取决于所采用的垃圾收集器是否具有空间压缩整理的能力决定的。
因此,当使用SerialParNew等带有压缩整理过程的收集器时,系统采用的内存分配方式是指针碰撞,简单高效。
而当使用CMS等基于清除算法的垃圾回收器时,理论上就只能采用较为复杂的空闲列表来分配内存

1.3 初始化

  1. 内存分配完成之后,虚拟机必须将分配到的内存空间(不包括对象头)初始化为零,如果使用了TLAB,初始化会在TLAB时顺便进行,初始化为零操作保证了对象的实例字段在java代码中不赋初值直接使用,使程序访问到这些字段数据类型所对应的零值
  2. 接下来,java虚拟机还要对对象进行必要的设置,例如这个对象是哪个类的实例,如何找到类的元数据信息,对象的哈希码(实际上的哈希码会延后到真正调用Object::hashCode()方法时才会计算),对象的GC分代年龄等信息,将会存放在对象的对象头(Object Header)中,根据虚拟机当前的运行状态不同,如是否使用偏向锁等,对象头会有不同的设置方式。
  3. 上面工作完成后,从虚拟机的角度看,一个新的对象已经产生了,但是从java角度来看,对象的创建才开始–构造函数(init<>方法)还未执行,所有的字段都是默认的零值,执行完init<>方法后一个真正可用的对象才被构建出来。
这篇关于java对象的创建过程的文章就介绍到这儿,希望我们推荐的文章对大家有所帮助,也希望大家多多支持为之网!