JVM虚拟机

面试问虚拟机如何处理new Object()?

2019-08-25  本文已影响0人  先生zeng

对象的创建

1.查询能否在常量池定位到该类的符号引用。
首先,在虚拟机遇到一个new指令时,首先会检查这个指令的参数是否能在常量池中定位到一个类的符号引用。这个符号引用代表该类是否已经被加载、解析和初始化过。如果没有就需要先执行类加载过程。

2.为对象分配内存空间。

如果java堆中的内存完整,那么会把指向空闲的指针往前移动一段与对象大小相等的距离。这种方式称为"指针碰撞".


正常状态


如果java堆中的内存不完整,已使用的内存与空闲内存相互交错,那么没办法直接进行指针碰撞。虚拟机会维护一个列表,记录那些内存块可用,在分配时,更新表上的记录,这块表称为空闲列表。


(拓展:选择哪种分配方式由Java堆是否规整决定,而Java堆是否规整又由所采用的垃圾收集器是否带有压缩整理功能决定。
因此,在使用Serial、ParNew等带Compact过程的收集器时,系统采用的分配算法是指针碰撞,而使用CMS这种基于Mark-Sweep算法的收集器时,通常采用空闲列表。)

3.初始化分配到的内存空间

内存分配完成后,虚拟机要将分配到的内存空间都初始化为0值(这里不包括对象头)这一步操作保证了对象的实例字段在Java代码中可以不赋初始值就直接使用,程序能访问到这些字段的数据类型所对应的零值。

4.对对象进行必要的设置

接下来,虚拟机要对对象进行必要的设置,例如这个对象是哪个类的实例、如何才能找到类的元数据信息、对象的哈希码、对象的GC分代年龄等信息。这些信息存放在对象的对象头(Object Header)之中。根据虚拟机当前的运行状态的不同,如是否启用偏向锁等,对象头会有不同的设置方式。

在上面工作都完成之后,从虚拟机的视角来看,一个新的对象已经产生了,但从Java程序的视角来看,对象创建才刚刚开始——<init>方法还没有执行,所有的字段都还为零。所以,一般来说(由字节码中是否跟随invokespecial指令所决定),执行new指令之后会接着执行<init>方法,把对象按照程序员的意愿进行初始化,这样一个真正可用的对象才算完全产生出来。

整个流程如图所示:

给对象分配内存会存在线程不安全的问题

解决 线程不安全 有两种方案:

1.同步处理分配内存空间的行为

2.虚拟机采用 CAS + 失败重试的方式 保证更新操作的原子性

同步处理:
把内存分配行为 按照线程 划分在不同的内存空间进行.
即每个线程在 Java堆中预先分配一小块内存(本地线程分配缓冲(Thread Local Allocation Buffer ,TLAB)),哪个线程要分配内存,就在哪个线程的TLAB上分配,只有TLAB用完并分配新的TLAB时才需要同步锁。

虚拟机是否使用TLAB,可以通过-XX:+/-UseTLAB参数来设定

部分内容取自:
来自《深入理解JVM虚拟机》JVM高级特性与最佳实现。
以及该博主:
https://baijiahao.baidu.com/s?id=1612824465692855405&wfr=spider&for=pc

上一篇下一篇

猜你喜欢

热点阅读