本文涉及:Java中的new命令之后发生的事
类加载检查
java虚拟机在遇到一条 new 指令时,首先会检查是否能在常量池中定位到这个类的符号引用,并且是否已被加载过、解析和初始化过。如果没有,那必须先执行类加载过程
类加载的相关知识可参考:JVM的类加载机制
分配内存
在类加载检查通过后,接下来虚拟机将为新生对象分配内存。
分配方式有 指针碰撞 和 空闲列表 两种
-
指针碰撞:Java堆中的内存是规整的,所有用过的内存都放在一边,空闲的内存放在另一边,中间放着一个指针作为分界点的指示器,分配内存也就是把指针向空闲空间那边移动一段与内存大小相等的距离。
-
空闲列表:Java堆中的内存不是规整的,已使用的内存和空闲的内存相互交错,就没有办法简单的进行指针碰撞了。虚拟机必须维护一张列表,记录哪些内存块是可用的,在分配的时候从列表中找到一块足够大的空间划分给对象实例,并更新列表上的记录
Java 堆内存是否规整,取决于 GC 的垃圾回收算法,在JVM中标记压缩、复制算法的内存是规整的、而标记清除则是不规整的
垃圾回收算法相关知识可参考:JVM垃圾收集算法详解
内存分配并发问题
-
CAS操作:CAS是属于原子性操作,更多关于此内容可参考此内容:Java原子类操作原理剖析
-
TLAB: 为每一个线程预先在Eden区分配一块儿内存,JVM在给线程中的对象分配内存时,首先在TLAB分配,当对象大于TLAB中的剩余内存或TLAB的内存已用尽时,再采用上述的CAS进行内存分配
内存空间初始化
内存分配完成后,虚拟机需要将分配到的内存空间都初始化为零值。这一步操作保证了对象的实例字段在 Java 代码中可以不赋初始值就直接使用,程序能访问到这些字段的数据类型所对应的零值。例如:
1
|
int a=1
|
设置对象头
内存空间初始化完成之后,虚拟机要在对象头中保存以下必要的信息:
-
这个对象是那个类的实例
-
如何才能找到类的元数据信息
-
对象的哈希码
-
对象的 GC 分代年龄
-
等等
执行构造方法
这里才是我们最熟悉的地方