对象分配概述
-
G1提供了两种分配策略java
- 基于线程本地分配缓冲区(Thread Local Allocation Buffer,TLAB)的快速分配
- 基于TLAB的慢速分配
- 慢速分配
- 当不能成功分配对象时就会触发垃圾回收

对象分配流程图
线程本地分配缓冲区(Thread Local Allocation Buffer,TLAB)
快速分配
指针碰撞法分配
- 若是TLAB剩余空间(end - top) 大于当前对象待分配空间。则直接修改 top = top + objSize(对象大小);
- 若是TLAB满了,则会保留这一部分空间,从新从堆内存中划一片空间给TLAB
如何判断TLAB满了
- 虚拟机内部会维护一个 refill_waste,当请求对象大于refill_waste,会选择在堆中分配,若小于该值,废弃当前的TLAB,新建一个TLAB来分配该对象。
- refill_waste 可使用 TLABRefillWasteFraction 参数来调整,默认值为64,即表示使用1/64的TLAB空间做为refill_waste
- 假设 TLAB为1M,那 refill_waste 为16k,即当TLAB使用了1008k时(1024k - 16k),就直接分配一个新的,不然就尽可能使用这个老的TLAB
如何调整TLAB
- 除了 TLABRefillWasteFraction ,JVM还提供了参数TLAB WasteIncrement(默认值为4个字),用于动态增长refill_waste
- refill_waste和TLAB的大小都会不断动态调整,使系统状态达到最优。
- 因为大对象都不会在新生代中,TLAB都不能分配大对象,因此TLAB的大小不会大于HRSize/2。(大于HRSize/2就会被认为是大对象)
TLAB中的慢速分配
- 若是TLAB中的剩余空间很小(TLAB满了),说明这个空间一般不知足对象分配,能够直接丢弃,填充一个dummy对象,而后申请一个新的TLAB来分配对象。
- 若是TLAB剩余空间比较多,那就不能丢弃TLAB,这时候就直接将对象分配到堆中,不使用TLAB,直接返回。
清理老的TLAB(dummy对象填充)
- GC在线性扫描堆的时候(好比查看HeapRegion对象,并行标记等),要知道哪里有对象,哪里是空白的。对于对象,扫描以后,能够直接跳过这个对象的长度,若是是空白的,就须要一个字一个字的扫,效率很低。因此把TLAB的空白地方分配一个dummy对象(哑元对象),这样GC就能作到快速遍历了。
申请一个新的TLAB缓冲区
调用
G1CollectHeap中分配,主要是在
attempt_allocation中完成
日志及解读
经过命令设置参数,以下所示:性能
-Xmx128M -XX:+UseG1GC -XX:+PrintGCDetails -XX:+PrintGCTimeStamps
-XX:+PrintTLAB -XX:+UnlockExperimentalVMOptions -XX:G1LogLevel=finest
能够获得:测试
garbage-first heap total 131072K, used 37569K [0x00000000f8000000,
0x00000000f8100400, 0x0000000100000000)
region size 1024K, 24 young (24576K), 0 survivors (0K)
TLAB: gc thread: 0x0000000059ade800 [id: 16540] desired_size: 491KB slow
allocs: 8 ref?ill waste: 7864B alloc: 0.99999 24576KB ref?ills: 50
waste 0.0% gc: 0B slow: 816B fast: 0Bd
分析日志中TLAB这个信息的每个字段含义:
- desired_size:指望分配的TLAB的大小,这个值就是咱们前面提到如何计算TLABSize的方式。在这个例子中,第一次的时候,不知道会有多少线程,因此初始化为1,desired_size=24576/50 = 491.5KB这个值是通过取整的。
- slow allocs:发生慢速分配的次数,日志中显示有8次分配到heap而没有使用TLAB。
- refill waste:retire一个TLAB的阈值。
- alloc:该线程在堆分区分配的比例。
- refills:发生的次数,这里是50,表示从上一次GC到此次GC期间,一共retire过50个TLAB块,在每个TLAB块retire的时候都会作一次ref?ill把还没有使用的内存填充为dummy对象。
-
waste:由3个部分组成:优化
- gc:发生GC时尚未使用的TLAB的空间。
- slow:产生新的TLAB时,旧的TLAB浪费的空间,这里就是新生成50个TLAB,浪费了816个字节。
- fast:指的是在C1中,发生TLAB retire(产生新的TLAB)时,旧的TLAB浪费的空间。
慢速分配
这里的慢速分配指的是在TLAB中分配,不能成功,最后进入慢速分配。(比TLAB慢速分配更慢
- attempt_allocation尝试进行对象分配,若是成功则返回。
- 若是大对象,在attempt_allocatin_humongous分配,直接分配老年代.
- 若是分配不成功,则进行GC垃圾回收(主要是FullGC),而后再分配。
- 最终成功,或者尝试N次后失败,则分配失败。
大对象分配流程(和TLAB相似,惟一区别就是对象大小不一样)
- 尝试垃圾回收(主要是增量回收,同时启动并发标记)
-
尝试开始分配对象spa
- 若是大于HRSize的一半且小于HRSize(即一个完整分区能够保存):直接从空闲列表得到一个分区,或者分配一个新分区。
- 若是是大于HRSize,则是个连续对象,须要多个分区,思路同上,可是须要加锁。
- 若是失败再从尝试垃圾回收开始。若是失败达到必定次数,则分配失败。
最后的分配尝试
- 尝试拓展新的分区,成功则返回
- 不成功进行则进行FullGC,可是不回收软引用,再次分配成功则返回
- 不成功再次进行FullGC,回收软引用,成功则返回
- 不成功则返回Null,分配失败
G1垃圾回收的时机
- 分配内存时发现内存不足
-
外部显式调用线程
-
java代码中的system.gc()指针
- 若是设置了DisableExplicitGC(默认为false),则不接受这个函数显式触发GC
- 默认为FullGC,若是设置了ExplicitGCInvokesConcurrent,表示能够进行并发的混合回收
-
和JNI交互,JNI代码进入了临界区
- 如JNI代码为了优化性能,提供了一个函数jni_GetPrimitiveArrayCritical/jni_GetStringCritical用于直接访问原始内存数据,可是为了保证安全必须使用GCLocker进行加锁。当加锁后发生了GC请求,此时GC会被延迟,直到GCLocker执行了unlock会从新补一个GC
- 若是设置了ExplicitGCInvokesConcurrent,表示能够进行并发的混合回收,若是没有设置,可能启动新生代回收
参数介绍和调优
- 在优化调试TLAB的时候,在调试环境中能够经过打开PrintTLAB来观察TLAB分配和使用的状况。
- 参数UseTLAB,指是否使用TLAB。大量的实验能够证实使用TLAB可以加速对象分配;该参数默认是打开的,不要关闭它。
- 参数ResizeTLAB,指是否容许TLAB大小动态调整。前面提到TLAB会进行动态化调整,主要是基于历史信息(分配大小、线程数等),有基准测试代表使用动态调整TLAB大小效率更高。
- 参数MinTLABSize,指设置TLAB的最小值。实际应用须要设置该值,好比64K,通常能够根据状况设置和调整该值。
- 参数TLABSize,指设置TLAB的大小。实际中不要设置TLABSize,设置以后TLAB就不能动态调整了,即会使用一个固定大小的TLAB,前面咱们提到GC能够根据状况动态调整TLAB,在分配效率和内存碎片之间找到一个平衡点,若是设置该值则这种平衡就失效了。
- 参数TLABWasteTargetPercent,指的是TLAB可占用的Eden空间的百分比,默认值是1。能够根据状况调整TLABWasteTargetPercent,增大则能够分配更多的TLAB,3.1节中给出了具体的计算方式;另外若是实际中线程数目不少,建议增大该值,这样每一个线程的TLAB不至于过小。
- 参数TLABRefillWasteFraction,指的是TLAB中浪费空间和TLAB块的比例,默认值是64。能够根据状况调整TLABRef?illWasteFraction,主要考量点是内存碎片和分配效率的平衡,若是发现日志waste中的slow和fast很大,说明浪费严重,能够适当减小该参数值。
- 参数TLABWasteIncrement,指的是动态的增长浪费空间的字节数,默认值是4。增长该值会增长TLAB浪费的空间;通常不用设置。
- 参数GCLockerRetryAllocationCount默认值为2,表示当分配中的垃圾回收次数超过这个阈值以后则直接失败。
TLAB不是G1才引入的,对象分配是JVM提供的基础分配功能,只不过G1结合本身内存分区的特征,以及垃圾回收的具体实现,从新实现了分配的策略,重用了这些参数的功能和使用方法,且没有引入额外的参数,因此这一部份内容不只适用于G1的调优,其余的垃圾回收器一样适用。