深入理解Java虚拟机 2.3.1章节笔记
在语言层面,对象的创建通常使用new关键字实现,这篇笔记主要记录虚拟机中一个对象的创建过程。
流程图如下:
1.当虚拟机接收到new指令时,首先会使用new指令的参数去常量池中定位类的符号引用。
2.检查这个类是否已经被记载并且初始化。
3.如果没有加载过这个类,需要执行相应的类加载的过程。
4.为新对象分配内存空间
-
分配空间有两种主要的方式- 指针碰撞(Bump the Pointer) 和 空闲列表(Free List)。
两种方式的选择取决于内存空间是否完全规整,即堆内存中不存在已分配空间和空闲空间交错的情况。
内存空间是否完全规整,取决于虚拟机的垃圾回收(GC)是否带有压缩整理功能(Serial, ParNew带有Compact, 基于Mark-Sweep的CMS则没有)
指针碰撞:假设jvm堆内存当前是绝对规整的,所有已分配对象都在一边,空闲空间在另一边,存在一个指针作为分界点的指示器,那么每一次分配内存都只需要将指针往空闲区域移动要分配的对象大小的范围即可。
空闲列表:由于堆内存中已分配对象和空闲区域相互交错,虚拟机需要维护一个列表来记录哪些内存块是可用的,在分配对象时虚拟机会从列表中找出一块可用空间,并维护列表的记录。
-
对象分配过程中的线程安全问题
对象的创建是一个非常频繁的行为,仅仅是修改一个指针指向的地址的操作在并发情况下也不是线程安全的行为,会出现正在给对象A分配内存,指针还没来得及修改,对象B又使用了原来的指针来分配内存。
-
两种解决方案:
- 对分配内存空间的动作进行同步处理,虚拟机上采用CAS配合失败重试来保证操作的原子性
- TLAB(Thread Local Allocation Buffer)本地线程分配缓冲,每个线程在Java堆中预先分配一块空间来进行实际对象的内存分配,当TLAB用完需要新的TLAB时才进行同步锁定。
5.内存分配完成后根据配置可能会需要将分配到的内存空间初始化为0值。
- 如果使用TLAB,可以提前至在TLAB中执行这一操作。
- 这一操作确保了对象的实例在java中可以不赋初始值而直接使用。因此程序可以访问到这些字段所对应的默认值
6.设置对象头(哪个类的实例,hash code,GC代年龄等)