对象的创建
如果JVM开启了栈上分配和标量替换
且经过JIT逃逸分析判定该对象的引用不会逃逸到线程外则该对象为栈分配候选;如果不满足栈上分配的条件,则尝试TLAB分配;如果TLAB分配不成功,则尝试堆上分配,如果满足进入老年代的条件,则对象直接分配到老年代,否则对象分配在新生代的eden区域。
图片参考:说说JVM对象创建
-
尝试栈上分配,分配失败则转入堆上分配
-
尝试TLAB本地线程分配缓冲分配
内存分配的动作按照线程划分在不同的空间之中进行,即每个线程在Java堆中预先分配一小块内存,称为本地线程分配缓冲TLAB。默认占eden区域大小的1%,分配时线程私有,使用时线程共享
-
TLAB分配失败,则判断是否小于最大浪费空间,如果小于则加锁在Eden区尝试申请一个TLAB存储对象
refill waste_ limit 是指最大的浪费空间,假设为5KB,通俗- -点讲
就是:
1、假如当前TLAB已经分配96KB,还剩下4KB,但是现在new了一
个对象需要6KB的空间,显然TL AB的内存不够了,这时可以简单的重新申请一一个TLAB,原先的TLAB交给Eden管理,这时只浪费4KB的空间,在_ refill waste_ limit 之内。
2、假如当前TLAB已经分配90KB,还剩下10KB,现在new了-个对
象需要11KB,显然TLAB的内存不够了,这时就不能简单的抛弃当前TLAB,这11KB会被安排到Eden区进行申请。 -
如果大于最大浪费空间,或者在Eden区存储失败
尝试通过指针碰撞和空闲列表来堆上分配内存
指针碰撞就是把Java内存区域当成完整的一块,空闲的内存和被使用的内存中间通过一个指针隔开,当创建一个新的对象的时候,将指针向空闲区域移动指定大小的空间。
空闲列表是当内存空间不规整,需要一个空闲列表记录哪些可用。
并发时移动指针不安全,解决方法:
一种是对分配内存空间的动作进行同步处理——实际上虚拟机是采用CAS配上失败重试的方式保证更新操作的原子性;
另外一种是TLAB
-
如果剩余空间不够则进行YoungGC,在判断空间是否够用,
;够用则堆上Eden区分配成功;
;不够用则需要进入老年代分配空间
6.分配完成后,将分配到的内存空间(但不包括对象头)都初始化为零值;给对象头存储必要信息(对象的哈希码(实际上对象的哈希码会延后到真正调用Object::hashCode()方法时才计算)、对象的GC分代年龄等)