1.类加载检查
虚拟机遇到一条 new 指令时,首先检查这个指令的参数常量池中找到一个类符号引用,并且检查这个符号引用代表的类是否已经被加载,解析,初始化过。如果没有必须先执行类的加载初始化过程。
2.分配内存
在类加载检查通过后,接着就可以为新生对象划分内存了,对象占用内存的大小在类加载后就可以完全确定。为对象分配内存空间就相当于把一块确定大小的内存从java堆中划分出来。
2.1如何划分内存呢
高并发的场景下如何保证同一块空间不会分给两个对象的呢?
指针碰撞 Bump the pointer
如果虚拟机堆中内存是绝对规整的,用过和没用过的各占一块完整的内存,中间放着一个指针作为分界点的指示器,在进行内存分配时,只需把指针向空闲区域移动一段距离,以放下新对象。
空闲列表 Free List
如果虚拟机堆中的内存不是规整的,用过的和没有用过的互相交错,就没有办法使用指针碰撞的方法;了。虚拟机必须维护一个列表,来记录队中有哪些区域是空闲的。在分配内存的时候找到一块足够大的空间分配给对象,并更新列表记录。
解决并发
分配对象在堆上,堆是线程共享的,多个线程可能同时会在一块内存上进行初始对象,造成后面线程将对象覆盖问题,这里会有多线程并发安全问题。虚拟机采用CAS配上失败重试的方式保证更新操作的原子性来对分配内存空间的动作进行同步处理。
本地线程分配缓冲(Thread Local Allocation Buffer TLAB)
每个线程预先在jvm堆中分配一块内存空间,线程声明周期内的对象分配都在这实现分配的空间中进行
3.内存空间初始化
内存分配完成后,虚拟机需要将分配到的内存空间都初始化为零值,这一步操作保证了对象的实例字段在java代码中可以不赋初始值就能直接使用。
4.设置对象头
在HotSpot虚拟机中,对象在内存中分布可以又3各部分祖成,对象头,实例数据,对齐填充。
4.1自身的运行数据(Mark word)
哈希码
GC分代年龄
锁状态标识
线程持有锁
偏向锁ID
偏向时间戳等
4.2类型指针
对象在方法区中对应类的指针,虚拟机通过这个指针来确定这个对象是哪个类的实例。
实例数据
存放对象的实际数据。
对齐填充
非必须得,也没有特别的含义,只是起着占位符的作用。
5.执行init方法
执行<init> 方法,就是按照程序员的意愿进行初始化,对应到语言层面上就是为属性赋值,和执行构造方法
网友评论