java如何引用对象 如何创建对象( 三 )


java如何引用对象 如何创建对象

文章插图
空闲列表分配:当内存使用的GC算法是标记清除算法时,内存是规整的,这个时候维护了内存空闲的列表,在为新对象分配内存时从空闲列表中找到内存就可以 。CMS使用的GC回收算法是标记清除算法,内存的分配方式就是空闲列表分配 。
java如何引用对象 如何创建对象

文章插图
看完内存的分配你有没有疑问?堆内存是所有线程共享的,如果两个线程同时都想占用这一块内存空间怎么办呢?这就涉及到了分配内存空间时的并发安全问题 。
JVM提供了两种处理并发安全的方式:一种是我们常用的CAS失败重试+区域锁来保证内存分配的原子性,另外一种是通过开启-XX:+UseTLAB参数为每个线程预分配一块TLAB,在JDK1.8中这个参数是默认开启的 。
经过了这一步之后,堆内存中就有了School实例的一块内存区域了:

java如何引用对象 如何创建对象

文章插图
初始化分配到的内存空间
属性的赋值操作分为3个类型,我们在示例中都有举例:
默认值初始化显式初始化和代码块初始化构造方法初始化
初始化分配到的内存空间是默认值初始化,它为类的成员变量设置默认值,保证对象实例字段在不赋值时可以直接使用 。基本数据类型的默认值为0,布尔类型的默认值为false,引用类型的默认值为null 。
不要把这一步的初始化和类加载过程中的初始化混淆了!
类加载过程中的初始化是对类的静态变量初始化,不包含类的实例变量 。
执行了这一步之后,内存中的情况如下图:

java如何引用对象 如何创建对象

文章插图
设置对象的对象头
将对象的所属的类、对象的HashCode值、对象的GC信息、锁信息等数据存放在对象头中 。它取决于JVM实现 。对象头的信息我们前面已经讲过,这里不再赘述 。
执行了这一步之后内存中的数据变化:

java如何引用对象 如何创建对象

文章插图
执行init进行初始化
这个时候初始化过程才真正开始 。这个过程是对应字节码invokespecial,执行init方法 。
它会执行实例化代码块、调用类的构造方法、将堆内对象的首地址赋值给引用变量 。这一步之后真正可用的对象才算创建完成 。
执行了这一步之后内存中的变化如下图:

java如何引用对象 如何创建对象

文章插图
总结
对象的创建过程:类元数据加载- 分配内存空间并解决并发问题- 初始化分配的内存空间- 设置对象头信息- 执行init方法进行初始化 。
对象的整个创建过程大家要对JVM的内存区域比较了解,熟悉每个区域存放的数据,并知道在哪个过程存的数据 。
类元数据的加载是元空间的数据来源,我们还可以回顾下类加载机制、双亲委派模型、哪些场景下需要打破双亲委派,之前勾勾分析了JDBC的SPI机制,利用线程上下文类加载器打破双亲委派 。
对象的创建都是基于堆空间的,我们可以回顾下堆空间的内存分配、GC回收算法和GC回收器 。
设置对象头信息我们需要了解对象头,还可以按照对象头的数据变化回顾synchronized锁的升级过程 。
对象创建之后内存的数据变化如下图:

java如何引用对象 如何创建对象

文章插图

java如何引用对象 如何创建对象

文章插图

推荐阅读