4

对象创建过程 - Carol淋

 2 years ago
source link: https://www.cnblogs.com/lin0/p/16687334.html
Go to the source link to view the article. You can view the picture content, updated content and better typesetting reading experience. If the link is broken, please click the button below to view the snapshot at that time.
neoserver,ios ssh client

对象创建过程

通常情况下,我们创建一个对象,只需要使用new关键字即可。而对于java虚拟机来说,需要经历一系列过程。 首先,需要找到对应的类是哪个,这个类是否已经加载,没有加载还需要将它先加载进来,然后给将要创建的对象分配内存,然后对对象进行初始化设置,我们才能使用一个完整的对象。

image-20220912171202584

大概java虚拟机遇到一条字节码new指令时,先根据指令的参数在常量池中定位一个类的符号引用,并且检查这个符号引用代表的类是否加载,解析和初始化过,如果没有需要先执行类加载过程(这里暂时不展开)。

经过查找类的一系列操作之后,java虚拟机知道了创建哪个类的对象,这时候需要内存来放置将要创建的对象。所以接下来需要分配内存,一个类加载进来之后,对象所需要的内存已经确定,现在需要找到一块空闲区域划分给将要产生的对象。

指针碰撞方式

这种方式需要将使用过的内存放在一边,未使用的放在另一边,指针指向第一个空闲位置,这时候分配内存只需要将指针往空闲位置移动对象大小位置即可。但是我们知道,内存有分配和回收,当不断的进行分配和回收之后,内存就不是连续的,如果我们使用这种方式显然行不通,所以我们需要采用的垃圾处理器带有空间压缩整理的功能,例如:Serial,ParNew

空闲列表方式

还有一种方式,空闲列表方式,根据名称我们都知道,用一个列表记录所有的空闲块,这个需要单独维护一个列表。CMS这种基于清除算法的收集器,就是采用了这个方式。但是有进行进一步的优化,为了能在大多数情况下分配的更快,设计了一个分配缓冲区,从空闲列表拿下一块较大的内存,然后在这块内存内部采用指针碰撞方式来进行分配

对象创建在虚拟机中是非常频繁的行为,即使使用最简单的指针碰撞方式,仅仅修改指针所指向的位置,在并发情况下也不是线程安全的(如果正在给对象A分配内存,还没修改指针位置,对象B也需要分配内存,相当于使用错误的指针位置)。

一种处理方式是进行同步处理,实际上虚拟机采用CAS+失败重试的方式保证更新操作的原子性。当对象A正在分配内存时,标识正在进行分配,对象B来时发现正在分配,则分配失败,继续重试,请求分配,直到对象A分配完毕,对象B继续进行分配。

另一种方式是把内存分配的动作按照线程划分在不同的空间中进行,即给每一个线程预先分配一小块内存,称为本地线程分配缓冲(Tread Local Allocation Buffer TLAB),哪个线程需要分配内存时候,现在自己的本地缓冲区中进行分配,不够了再进行同步锁定方式。虚拟机是否使用TLAB可以通过- XX:+/-useTLAB参数进行设定。

当给对象分配了空间之后,我们需要对对象进行初始化操作,初始化对象的有三个步骤

将除了对象头以外的所有内存空间初始化为零值(如果使用了TLAB,这一步操作可以提前到TLAB分配是进行),这步操作保证了对象的实例字段在java代码中可以不赋值直接使用。

设置对象头

对象头,既然位于最前面,应该存储一些描述对象的基本信息,其中包括该对象是哪个类的实例,如何找到类的元信息,对象的hash码(实际不是在这里进行设置,而是在调用Object::hashCode()时计算),对象的GC分代年龄,锁等信息。到这一步,在虚拟机层面一个新的对象已经产生了。

最后一步,才是按照开发人员真正的意愿去构造对象需要的其他资源(父类等)和状态信息,这样一个真正可用的对象才构建出来。

对象的创建过程很简单,就是找到创建依据(类),放在哪里(分配内存),初始化(设置我们想要的东西)。


About Joyk


Aggregate valuable and interesting links.
Joyk means Joy of geeK