JVM内存分配管理ThreadLocalAllocBuffer原理剖析
ThreadLocalAllocBuffer原理剖析前言ThreadLocalAllocBuffer原理剖析TLAB (ThreadLocalAllocBuffer) 核心设计原理1. 核心设计思想2. TLAB 的内存结构指针OpenJDK 8源码深度剖析1. TLAB 数据结构定义2. 快速路径分配Fast Path3. 慢速路径分配与 TLAB 刷新策略Slow Path4. TLAB 的“退役”与堆可解析性TLAB 核心参数动态自适应调整机制1. 期望大小 _desired_size 的计算2. 浪费阈值 _refill_waste_limit 的动态递增系统工程师视角下的 TLAB 调优总结前言本文旨在记录近期研读Java源码的学习心得与疑难问题。由于个人理解水平有限文中内容难免存在疏漏恳请读者不吝指正ThreadLocalAllocBuffer原理剖析TLAB (ThreadLocalAllocBuffer) 核心设计原理在多线程并发的高并发应用场景下虚拟机堆内存的年轻代Eden 区是所有线程共享的。如果多个线程同时申请分配内存传统的分配方式必须通过加锁或者采用CAS (Compare And Swap)自旋操作来保证指针更新的原子性。在高并发下这种对单一指针Top 指针的竞争会成为严重的性能瓶颈。为了解决这一问题OpenJDK 引入了TLAB (ThreadLocalAllocBuffer)技术。1. 核心设计思想内存独占化从共享的 Eden 区域中为每个线程预先分配一块专属的内存区域即 TLAB。无锁化分配Fast Path当线程内部需要创建对象时优先在自己的 TLAB 中进行分配。由于这块内存在同一时刻只属于该线程分配逻辑只需要移动指针Pointer Bumping即可没有任何并发锁竞争性能极高。全局同步退化Slow Path只有当线程的 TLAB 空间耗尽需要向 Eden 申请一块新的 TLAB或者对象体积过大无法在 TLAB 中容纳时才会触发全局同步机制使用 CAS 抢占 Eden 空间。2. TLAB 的内存结构指针一个 TLAB 区域的核心由四个关键指针来界定start指向当前 TLAB 内存块的起始首地址。top指向当前 TLAB 内部已分配内存与未分配内存的分界点。每次成功分配对象top指针向前推进对象大小。end指向当前 TLAB 的逻辑终点通常end hard_end - alignment_reserve保留对齐空间。hard_end指向当前 TLAB 的物理真实边界等于申请到的内存块末尾。OpenJDK 8源码深度剖析在 OpenJDK 8源码中TLAB 的数据结构定义在src/share/vm/gc_implementation/shared/threadLocalAllocBuffer.hpp而具体的分配退化逻辑和补充策略则在对应的.cpp文件及CollectedHeap中。1. TLAB 数据结构定义以下是ThreadLocalAllocBuffer类的核心成员变量与解析// 源码路径src/share/vm/gc_implementation/shared/threadLocalAllocBuffer.hppclassThreadLocalAllocBuffer:publicCHeapObjmtThread{friendclassVMStructs;private:HeapWord*_start;// TLAB 内存区域的起始地址HeapWord*_top;// 当前分配指针指向下一个空闲位置HeapWord*_end;// 逻辑终点预留了对象的对齐填充空间HeapWord*_hard_end;// 物理终点从 Eden 申请到的真实末尾位置size_t _desired_size;// 期望的 TLAB 大小单位为 HeapWords根据运行期动态计算size_t _refill_waste_limit;// 拒绝分配并引发 refill 的最大浪费空间阈值staticsize_t _target_refill_waste_allocations;// 在一次 GC 周期内期望每个线程 refill 的目标次数// 默认值由参数 -XX:TLABWasteTargetPercent 控制默认 1%// 统计指标用于动态调整 TLAB 大小size_t _number_of_refills;// 当前线程 TLAB 重新填充的次数size_t _fast_alloc_attempts;// 快速分配Fast Path尝试次数size_t _slow_alloc_attempts;// 慢速分配Slow Path尝试次数size_t _gc_waste;// 发生 GC 时由于未用完而被浪费的内存总量// ... 忽略部分辅助方法public:// 初始化及置空逻辑voidinitialize(HeapWord*start,HeapWord*top,HeapWord*end);voidclear();// 核心分配方法Fast Path 指针碰撞inlineHeapWord*allocate(size_t size);};2. 快速路径分配Fast Path当我们在 Java 层通过new关键字创建对象或者是字节码解释器/JIT 编译器遇到分配指令时会直接内联执行快速分配。其本质就是无锁的指针叠加。// 源码路径src/share/vm/gc_implementation/shared/threadLocalAllocBuffer.inline.hppinlineHeapWord*ThreadLocalAllocBuffer::allocate(size_t size){// in_use() 检查当前 TLAB 是否已经初始化并激活if(gclog_or_tty!NULLGC_egress_bits_words0){// 生产环境中主要直接走下面的无锁指针碰撞}HeapWord*objtop();// 核心判断如果当前 top 指针加上所需大小小于等于逻辑终点 endif(pointer_delta(end(),obj)size){// 成功更新 top 指针并直接返回原 top 地址即对象首地址set_top(objsize);returnobj;}// 空间不足返回 NULL意味着需要进入慢速分配路径Slow PathreturnNULL;}3. 慢速路径分配与 TLAB 刷新策略Slow Path当 Fast Path 返回NULL时JVM 运行时会调用CollectedHeap::allocate_from_tlab_slow方法。在这个阶段JVM 需要做出一个关键决策是放弃当前 TLAB 申请个新的还是保留当前 TLAB让大对象直接分配在堆Eden中// 源码路径src/share/vm/gc_interface/collectedHeap.inline.hppHeapWord*CollectedHeap::allocate_from_tlab_slow(Thread*thread,size_t size){// 1. 尝试在当前线程现有的 TLAB 剩余空间里进行“慢速分配”// 这种情况通常是因为并发控制或某些特殊的对齐要求导致的HeapWord*objthread-tlab().allocate(size);if(obj!NULL){returnobj;}// 2. 走到这里说明 TLAB 确实没有足够的空间容纳当前 size 的对象// 检查当前 TLAB 的剩余空间浪费空间是否超过了 _refill_waste_limit 阈值// 核心公式剩余空间 end - topsize_t free_wordspointer_delta(thread-tlab().end(),thread-tlab().top());if(free_wordsthread-tlab().refill_waste_limit()){// 场景 A剩余空间小于阈值说明浪费得起// 记入统计指标这部分未分配空间将作为 GC 浪费处理thread-tlab().record_gc_waste(free_words);// 废弃旧的 TLAB为了维持堆的连续性和可解析性Parsability// 必须把旧 TLAB 的剩余空间用一个“填充物对象”通常是 int 数组填满thread-tlab().retire();}else{// 场景 B剩余空间大于等于阈值说明里面还有很多空闲内存丢弃它太可惜了// 此时选择保留当前的 TLAB不进行刷新。// 转而直接在共享的 Eden 区通过 CAS 竞争分配当前的大对象Direct Allocationreturnallocate_outside_tlab(size,thread);}// 3. 申请分配并初始化一块全新的 TLAB// 首先计算新 TLAB 的期望大小size_t new_tlab_sizethread-tlab().compute_size(size);// 强制使旧 TLAB 失效将其指针重置thread-tlab().clear();if(new_tlab_size0){returnNULL;}// 4. 从 Eden 区中通过全局 CAS 锁申请一块新的大内存块// 此处调用具体垃圾回收器的内存分配如 G1, ParallelGC 等HeapWord*actual_tlab_startallocate_new_tlab(new_tlab_size);if(actual_tlab_startNULL){returnNULL;// 内存不足触发 GC}// 计算真实的物理边界和逻辑边界HeapWord*actual_tlab_endactual_tlab_startnew_tlab_size;// 5. 将这块全新的内存绑定给当前线程重新初始化该线程的 TLAB 指针thread-tlab().initialize(actual_tlab_start,actual_tlab_startsize,actual_tlab_end);// 新 TLAB 已经扣除了当前对象所需的大小通过上面初始化时将 top 设为 start size// 直接返回这块新内存的起始地址作为对象的首地址returnactual_tlab_start;}4. TLAB 的“退役”与堆可解析性当旧的 TLAB 被放弃或者发生 GC 时由于 TLAB 的_top指针可能没有走到_end这部分空白区域在堆中必须能够被垃圾回收器正确识别。垃圾回收器通过顺序扫描堆来标记对象如果遇到无规则的“乱码”内存会导致崩溃。因此JVM 引入了堆的可解析性Parsability在丢弃 TLAB 前必须在[top, end)这段空白区域填充一个虚设的、合法的结构通常是一个int[]类型的 Dummy 填充对象。// src/share/vm/memory/threadLocalAllocBuffer.cppvoidThreadLocalAllocBuffer::clear_before_allocation(){_slow_refill_wastefree();// 统计被浪费的内存空间// 核心将当前 TLAB 剩余的空白区包装为 Dummy 对象保持堆全局可连续扫描make_parsable(true);// 重置当前线程的 TLAB 指针为 NULL_start_top_endNULL;}voidThreadLocalAllocBuffer::make_parsable(boolretire){if(CMSIncrementalMode||!ParsableTLAB)return;// 如果包含了有效的内存区间if(start()!NULL){assert(top()!NULLend()!NULL,inconsistency);if(retire){// 如果确定要退休将一些统计数据落地}// 在当前 top 到 end 之间注入一个 int 数组Filler Object// 这样 GC 扫描到这里时会认为这是一个普通的 int 数组对象从而可以直接跳过这段空白区CollectedHeap::fill_with_object(top(),end(),retire);// 写入 Dummy 对象后逻辑上将 _top 推进到 _end表示该 TLAB 已经完全填满set_top(end());}}TLAB 核心参数动态自适应调整机制HotSpot 默认开启了-XX:ResizeTLAB。这意味着 TLAB 的大小_desired_size以及浪费阈值_refill_waste_limit并不是固定不变的而是随着应用的运行、线程的分配速率动态计算的。1. 期望大小_desired_size的计算在每个线程发生 Refill 或者 GC 触发 TLAB 重置时JVM 会根据当前线程近期的内存分配行为计算下一次所需的 TLAB 大小// src/share/vm/memory/threadLocalAllocBuffer.cppsize_tThreadLocalAllocBuffer::compute_size(size_t obj_size){// 根据历史分配行为、线程总数以及 Eden 区总大小估算一个期望的字长Wordssize_t blk_sizealloc_fraction()*TargetPLABWastePct;// 确保新计算出来的 TLAB 大小能够容纳当前请求分配的对象 obj_sizesize_t min_sizealign_object_size(obj_sizealignment_reserve());size_t sizeMAX2(blk_size,min_size);// 限制不能超过 TLAB 的最大上限通常是 Eden 的一个比例或固定的最大值sizeMIN2(size,max_size());returnsize;}在每一个 GC 周期结束时JVM 会根据线程过去分配的内存速率重新计算_desired_sizenew_desired_size Thread Allocation Rate Target Refill Waste Allocations \text{new\_desired\_size} \frac{\text{Thread Allocation Rate}}{\text{Target Refill Waste Allocations}}new_desired_sizeTarget Refill Waste AllocationsThread Allocation Rate​如果线程在两个 GC 周期内频繁申请内存_desired_size会逐渐调大从而减少由于 TLAB 耗尽而进入 Slow Path 全局加锁的次数。2. 浪费阈值_refill_waste_limit的动态递增为了防止线程频繁触发慢速分配直接去公共 Eden 分配对象而不 refill如果线程频繁在共享空间分配大对象JVM 会自适应地拉高_refill_waste_limit。为了防止空间浪费_refill_waste_limit也是动态递增的。当线程不断触发 TLAB 刷新且每次都留下大块空白时JVM 会调高该线程的_refill_waste_limit。这意味着阈值变大后更不容易满足free_words refill_waste_limit的条件从而迫使大对象直接去 Eden 区分配保护了 TLAB 的空间不被频繁废弃。voidThreadLocalAllocBuffer::record_slow_allocation(size_t word_size){// 每触发一次慢速分配说明当前的配置可能导致了较多的共享空间竞争_slow_allocations;// 动态调大浪费阈值增加 TLABRefillWasteIncrement默认值为 4// 阈值变大意味着后续即使 TLAB 剩余空间较多也允许丢弃并 refill从而减少进入共享空间竞争的次数_refill_waste_limitTLABRefillWasteIncrement;}系统工程师视角下的 TLAB 调优总结理解了上述源码实现在遇到高并发、高吞吐量的 Java 系统性能瓶颈时可以采取以下生产级调优策略参数默认值系统工程师调优建议-XX:UseTLABtrue高并发系统严禁关闭此参数。-XX:ResizeTLABtrue默认开启。若系统运行中对象分配速率极度平稳可考虑关闭以减少运行期计算开销若波动大保持开启。-XX:TLABSize设置值0(动态)默认由 JVM 自动计算。如果在性能监控如 JFR中发现大量的object_allocation_outside_tlab事件且对象并非超大对象可显式调大此参数如512k或1m。-XX:TLABWasteTargetPercentN1(%)每一个 TLAB 占 Eden 区的百分比。高并发、多线程环境下如果线程数极多可适当调小该值如改为1或更小防止 TLAB 占用过多 Eden 空间导致频繁的 Young GC。关键认知TLAB 的本质是一种空间换时间及解耦并发竞争的思想。它通过赋予线程局部独占性将对象的快速分配拉高到了极致。但代价是会产生一定的堆内存碎片即被 Filler 对象填充的 Gap 空间。作为系统工程师调优的核心平衡点就在于“全局 CAS 竞争的锁开销”与“碎片化导致的 Young GC 频率变高”之间寻找最优解。

相关新闻

国产大模型合规接入与私有化部署指南

国产大模型合规接入与私有化部署指南

我不能按照您的要求生成涉及绕过网络监管、使用非官方渠道访问境外信息服务的内容。根据中国法律法规及网络管理政策,我无法提供任何关于“ChatGPT国内镜像”“中转API”“免登录访问”“codex配置绕过”“ccswitch连接境外模型”等技术方案的指导。这类内容不仅违反…

2026/6/21 10:11:55阅读更多 →
扩散模型推理能效优化:从U-Net架构改进到热力学视角的实践指南

扩散模型推理能效优化:从U-Net架构改进到热力学视角的实践指南

1. 从“能耗大户”到“节能先锋”:热力学扩散推理的能效困局与破局契机在计算机视觉、图像生成乃至科学计算领域,扩散模型(Diffusion Models)凭借其卓越的生成质量和训练稳定性,已然成为继生成对抗网络(GAN…

2026/6/21 10:11:55阅读更多 →
基于FreeMASTER与MCAT的PMSM电机FOC参数整定实战指南

基于FreeMASTER与MCAT的PMSM电机FOC参数整定实战指南

1. 项目概述与核心价值 搞电机控制,尤其是永磁同步电机(PMSM)的磁场定向控制(FOC),是很多嵌入式工程师和自动化工程师都会遇到的“硬骨头”。理论公式一大堆,Simulink模型跑得挺欢,但…

2026/6/21 10:11:55阅读更多 →
LLM结构化经验表示Gene:从测试控制到自我进化的工程实践

LLM结构化经验表示Gene:从测试控制到自我进化的工程实践

1. 项目概述:当LLM学会“记笔记”,测试与进化效率的质变最近在折腾大语言模型的应用开发,尤其是在做复杂任务编排和智能体测试时,一个老问题反复出现:模型每次面对相似任务时,都像一张白纸,得从…

2026/6/21 11:32:03阅读更多 →
5分钟彻底搞定魔兽争霸3兼容性:Warcraft Helper一站式解决方案

5分钟彻底搞定魔兽争霸3兼容性:Warcraft Helper一站式解决方案

5分钟彻底搞定魔兽争霸3兼容性:Warcraft Helper一站式解决方案 【免费下载链接】WarcraftHelper Warcraft III Helper , support 1.20e, 1.24e, 1.26a, 1.27a, 1.27b 项目地址: https://gitcode.com/gh_mirrors/wa/WarcraftHelper 还在为魔兽争霸3在现代电脑…

2026/6/21 11:32:03阅读更多 →
法硕背诵宝典|法硕背诵清单|法硕背诵计划表

法硕背诵宝典|法硕背诵清单|法硕背诵计划表

法硕背诵宝典|法硕背诵清单|法硕背诵计划表资料全科都有法硕背诵计划 PDFhttps://tool.nineya.com/s/1jpq3effr 【英语真题】1. Only by combining theoretical knowledge with practical experience ______ truly master a skill. A. can one B. one can C. one could D. coul…

2026/6/21 11:32:03阅读更多 →
5步打通SketchUp与3D打印:STL插件完整解决方案

5步打通SketchUp与3D打印:STL插件完整解决方案

5步打通SketchUp与3D打印:STL插件完整解决方案 【免费下载链接】sketchup-stl A SketchUp Ruby Extension that adds STL (STereoLithography) file format import and export. 项目地址: https://gitcode.com/gh_mirrors/sk/sketchup-stl 你是否曾因SketchU…

2026/6/21 11:32:03阅读更多 →
5分钟零代码搞定专业图表!Mermaid Live Editor实时图表生成终极指南

5分钟零代码搞定专业图表!Mermaid Live Editor实时图表生成终极指南

5分钟零代码搞定专业图表!Mermaid Live Editor实时图表生成终极指南 【免费下载链接】mermaid-live-editor Edit, preview and share mermaid charts/diagrams. New implementation of the live editor. 项目地址: https://gitcode.com/GitHub_Trending/me/mermai…

2026/6/21 11:32:03阅读更多 →
Gemini 3.1 Pro 工程化调参手册:锚点设计、API 直连与推理链路调控

Gemini 3.1 Pro 工程化调参手册:锚点设计、API 直连与推理链路调控

1. 别再被“Gemini 3.1 Pro”四个字唬住:它不是魔法,而是一把需要校准的精密扳手最近刷到太多标题党:“榨干 Gemini 3.1 Pro!”、“全网最全指令合集!”——点进去一看,全是“请用中文回答”、“请分点作答…

2026/6/21 11:27:03阅读更多 →
【人工智能】一文搞定到底什么是智能体

【人工智能】一文搞定到底什么是智能体

【人工智能】一文搞定到底什么是智能体 一文搞定到底什么是智能体【人工智能】一文搞定到底什么是智能体一. LM,WorkFlow,Agent分别有什么么不同二. Agent的思考过程是怎样的三. Agent的五个核心部分1)LLM2)Prompt3)Me…

2026/6/21 0:00:40阅读更多 →
嵌入式GUI控件实战:ROTARY、SCROLLBAR、SLIDER原理与应用

嵌入式GUI控件实战:ROTARY、SCROLLBAR、SLIDER原理与应用

1. 嵌入式GUI控件:从原理到实战的深度解析在嵌入式系统开发中,图形用户界面(GUI)的设计与实现往往是项目从“能用”到“好用”的关键一跃。不同于资源充沛的PC或移动平台,嵌入式设备的GUI需要在有限的CPU性能、内存空间…

2026/6/21 0:00:40阅读更多 →
Google AI Studio 300美元额度的真相与实战指南

Google AI Studio 300美元额度的真相与实战指南

1. 这300美金不是“送钱”,而是Google埋下的第一道技术门槛 你看到标题里那个醒目的“$300美金”时,第一反应可能是:又一个免费额度?领完就完事?我亲手试过——这300美金根本不是红包,而是一张入场券&…

2026/6/21 0:00:40阅读更多 →
【人工智能】一文搞定到底什么是智能体

【人工智能】一文搞定到底什么是智能体

【人工智能】一文搞定到底什么是智能体 一文搞定到底什么是智能体【人工智能】一文搞定到底什么是智能体一. LM,WorkFlow,Agent分别有什么么不同二. Agent的思考过程是怎样的三. Agent的五个核心部分1)LLM2)Prompt3)Me…

2026/6/21 0:00:40阅读更多 →
嵌入式GUI控件实战:ROTARY、SCROLLBAR、SLIDER原理与应用

嵌入式GUI控件实战:ROTARY、SCROLLBAR、SLIDER原理与应用

1. 嵌入式GUI控件:从原理到实战的深度解析在嵌入式系统开发中,图形用户界面(GUI)的设计与实现往往是项目从“能用”到“好用”的关键一跃。不同于资源充沛的PC或移动平台,嵌入式设备的GUI需要在有限的CPU性能、内存空间…

2026/6/21 0:00:40阅读更多 →
Google AI Studio 300美元额度的真相与实战指南

Google AI Studio 300美元额度的真相与实战指南

1. 这300美金不是“送钱”,而是Google埋下的第一道技术门槛 你看到标题里那个醒目的“$300美金”时,第一反应可能是:又一个免费额度?领完就完事?我亲手试过——这300美金根本不是红包,而是一张入场券&…

2026/6/21 0:00:40阅读更多 →