前言
- 堆是线程共享的,在堆上给对象分配内存时需要加锁(通过cas实现),因此new一个对象的开销是比较大的。
概念:
- 为了提升对象内存分配的效率,jvm在创建线程的时候会在eden区给每个线程分别划出一块独立的空间,这个独立的空间称为线程本地分配缓冲区(Thread Local Allocation Buffer)。
- 一个TLAB只允许一个线程在其中分配内存,故在TLAB上分配内存时不需要加锁,因此在TLAB上给对象分配内存的性能接近C的性能。
堆上分配内存的流程:

说明:
- TLAB只允许一个线程在其中进行内存分配,但是允许所有的线程来访问TLAB中的对象。
- TLAB空间的内存非常小,默认仅占有整个Eden空间的1%。
- TLAB的大小由JVM根据运行的情况计算而得。
- 如果对象过大的话则仍然是直接使用堆空间分配。
- 当一个TLAB剩余的空间不够使用时,jvm就会给对应的线程再申请一个TLAB,而之前的TLAB自动退化为普通的eden区。
- 引入TLAB 后,会有内存孔隙问题,还可能影响 GC 扫描性能,出现孔隙的情况:

- 当前 TLAB 不够分配时,如果剩余空间小于最大浪费空间限制,那么这个 TLAB 会被退回 Eden,重新申请一个新的。这个剩余空间就会成为孔隙。
- 当发生 GC 的时候,TLAB 没有用完,没有分配的内存也会成为孔隙。
- 如果不管这些孔隙,由于 TLAB 仅线程内知道哪些被分配了,在 GC 扫描发生时返回 Eden 区,如果不填充的话,外部并不知道哪一部分被使用哪一部分没有,需要做额外的检查,那么会影响 GC 扫描效率。所以 TLAB 回归 Eden 的时候,会将剩余可用的空间用一个 dummy object 填充满。如果填充已经确认会被回收的对象,也就是 dummy object, GC 会直接标记之后跳过这块内存,增加扫描效率。但是同时,由于需要填充这个 dummy object,所以需要预留出这个对象的对象头的空间。
参数:
- -XX:UseTLAB
- -XX:TLABWasteTargetPercent
优点:
- 小对象可以直接在TLAB中分配,故一般情况下给多个小对象分配内存要比给一个大对象分配内存更有效率。
创建的时机:
- 线程初始化时会创建TLAB。
- gc时,在对象扫描完成之后,当线程第一次尝试分配对象的时候会创建TLAB。
相关资料:全网最硬核 JVM TLAB 分析 - 哔哩哔哩