java对象分配优先级为:先逃逸分析决定是否栈上分配;否则走tlab分配;tlab失败则在eden区分配;大对象或eden不足时直接进入老年代。

Java对象不是简单调用new就直接进堆里某个固定位置的。它的实际落点,取决于逃逸分析结果、对象大小、线程状态和JVM运行时策略,整体流程有明确优先级和 fallback 机制。
栈上分配:首选但受限的优化路径
JVM在创建对象前,会先做逃逸分析——判断该对象是否只在当前方法内使用、不会被其他线程或方法持有引用。若确认不逃逸,且开启相关优化(JDK7+默认启用-XX:+DoEscapeAnalysis),则可能走栈上分配:
- 对象被“打散”,通过标量替换(
-XX:+EliminateAllocations)拆成基本类型字段,直接分配在当前线程的栈帧中 - 方法执行结束,栈帧弹出,内存自动释放,完全绕过GC
- 仅适用于小而短生命周期的局部对象;大对象、有逃逸行为的对象(如作为返回值、存入静态集合)无法栈上分配
TLAB 分配:多线程下的高效默认路径
未走栈上分配的对象,绝大多数进入堆的新生代 Eden 区——但不是直接抢同一块共享内存,而是优先使用线程私有的 TLAB(Thread Local Allocation Buffer):
- 每个线程启动时,JVM从 Eden 划分一块专属缓冲区(默认约 Eden 总空间的1%,可调
-XX:TLABSize) - 分配时仅移动本地指针(指针碰撞),无锁、无竞争,性能极高
- TLAB用尽后,线程会申请新TLAB;若 Eden 剩余空间不足,则退化为在共享 Eden 区分配(需 CAS 同步)
- TLAB本身不改变对象生命周期,GC 时仍按常规方式扫描整个 Eden(含所有线程的TLAB区域)
Eden 与老年代:兜底与特殊规则
当TLAB不可用或不适用时,分配逻辑转向共享堆区域:
- 普通小对象:直接在 Eden 共享区分配,采用指针碰撞(内存规整)或空闲列表(内存碎片化)方式
-
大对象(如大数组):若超过
-XX:PretenureSizeThreshold阈值,直接分配到老年代,避免在新生代反复复制 -
TLAB浪费率过高或显式关闭(
-XX:-UseTLAB)时,所有分配都走共享 Eden,易引发 CAS 竞争,压测中需关注
如何验证与调优分配行为
分配过程不可见,但可通过 JVM 参数输出关键线索:
-
-XX:+PrintGCDetails -XX:+PrintGCTimeStamps:观察 GC 日志中的 “allocation failure” 触发原因及各代占用变化 -
-XX:+PrintTLAB:打印每线程 TLAB 大小、refill 次数、浪费率,识别是否频繁 refill 或空间闲置 -
-Xlog:gc*,gc+ref=debug(JDK9+):更细粒度跟踪对象分配位置与晋升路径
Java免费学习笔记:立即使用
解锁 Java 大师之旅:从入门到精通的终极指南











