JVM(二):对象的创建

以前想的是:对象都是new出来的,执行一系列的代码块和构造方法然后得到对象的实例。对象的实例是在堆内存上的。

是的,没有错。在明面上我们所知道的就是这些,但是在JVM的角度来看,在这之前,它还为我们做了很多的事情,所以现在我们来看一看虚拟机在对象的创建过程中做了什么吧?


本文参考了周志明老师的《深入理解Java虚拟机:JVM高级特性与最佳实践

以下很多内容都是参考这本书所写的。推荐大家去阅读。

对象创建

一:当JVM遇到new这样的指令时,首先会检查常量池中是否能够定位到一个类的符号引用,然后会检查对应的那个类是否已经被加载、解析和初始化。(类加载过程后面再回来补这段知识点,接着看)


二:类加载检查过后,虚拟机会为新生对象分配内存。新生对象所需的内存大小在类加载完的时候就已经确定下来了,然后会将这份内存从Java堆中划分出来,这里有两种情况:

①Java堆内存是绝对规整的,会使用“指针碰撞”的方式来分配内存。

②如果Java堆内存不是规整的,会使用“空闲列表”的方式来分配内存。


“指针碰撞”指的是Java堆中,使用过的内存放在一边,然后空闲的内存放在另外一边,两者中间放置一个指针作为分界的指示器,分配内存的时候就将指针往空闲内存那边移动与新生对象所需内存大小相等的距离。

画个简单的图来看一下吧:


相信大家也会想到,这样分配内存会出现什么问题?因为在分配内存的时候,不会慢悠悠的一个一个的轮流慢慢的分配内存的;是的,在虚拟机创建对象是非常频繁的。所以这样的情况是线程不安全的。

解决方案有两种:(引用书本原话)

①对分配内存空间的动作进行同步处理,就是虚拟机采用了CAS配上失败重试的方式来保证更新操作的原子性。

②把内存分配的动作按照线程划分在不同的空间之中进行,即每个线程在Java堆中预先分配一小块内存,称为本地线程分配缓冲(TLAB)。哪个线程需要分配内存,就在哪个线程的TLAB上分配,只有当TLAB用完并分配新的TLAB时,才需要同步锁定。


“空闲列表”指的是在内存不规整(已使用的内存和空闲内存相互交错)的情况下,虚拟机必须要维护一个列表来记录可用的内存块,在分配内存的时候从列表上找到足够大的空间来分配给新生对象。最后还需要更新这个列表。


三、内存分配完成后,虚拟机需要将分配到的内存空间初始化(不包括对象头),如果使用了TLAB,这一步可以放到TLAB分配的时候完成。这步的作用就是类似下面这段代码中,你没有赋值给name,name还能返回null。

/**
 * 
 * @author dh
 *
 */
public class TestGCWithDH {
	public static void main(String[] args) {
		User user = new User();
		System.out.println(user.name);
	}
}
class User{
	public String name;
}

输出结果:null


四、虚拟机接下来会对对象进行必要的设置,如这个对象是哪个类的实例,如何能够找到类的元数据信息,对象的哈希码、对象的GC分代年龄等信息。    

五、然后会按照程序员的意愿来执行初始化方法。。。对象真正的创建出来了。


猜你喜欢

转载自blog.csdn.net/Adrian_Dai/article/details/80239330