跳到正文
Elaine Blog
返回

对象创建、布局、逃逸分析与分配路径

JVM 与性能

源码中的 new 不等于一次昂贵的系统内存申请。HotSpot 通常在线程本地分配缓冲区中移动指针完成小对象分配,JIT 还 可能消除未逃逸对象。性能判断必须以分配采样和基准结果为证据。

Table of contents

Open Table of contents

一次对象创建的逻辑步骤

执行 new 字节码时,JVM 先确保目标类已加载并初始化,再为实例分配空间、把字段设为默认值、写入对象头,最后调用 构造器 <init>

对象通常包含三部分:

这些是 HotSpot 实现细节,压缩类指针、对象对齐和 JDK 版本都会影响实际布局。不要在容量规划中使用网上固定的 “对象头永远 12 字节”结论。

TLAB 降低分配竞争

**TLAB(Thread-Local Allocation Buffer,线程本地分配缓冲区)**是 Eden 中预留给单个线程的一小块区域。线程在自己 的 TLAB 内分配时通常只需移动指针,不需要与其他线程竞争同一个分配位置。

观察当前 JVM 的相关参数:

java -XX:+PrintFlagsFinal -version | grep -E 'UseTLAB|ObjectAlignment|Compressed'

日志和参数名属于 HotSpot,不是 Java 语言契约。容器环境中还应同时记录 JDK 完整版本和发行版。

对象如何晋升

新对象通常先进入年轻代。经过收集仍存活的对象可能在 Survivor 区复制并增加年龄,最终进入老年代;大对象或分配失败 路径可能不同。G1 以 Region 组织堆,超过 Region 一半大小的对象属于 Humongous Object,会占用连续 Region 并可能 提前触发收集。

使用实验室的GC 压力样例下载

java -Xms128m -Xmx128m -Xlog:gc+age=trace,gc+heap=debug \
  -cp target/classes dev.elaine.jvmlab.GcPressureSample 5

你应看到年轻代收集和对象年龄信息。具体次数取决于机器与 JDK,不要把示例次数写成性能目标。

逃逸分析可能消除对象

若对象没有逃出方法或线程,JIT 可以进行标量替换,把对象字段拆成独立值,甚至完全消除分配。查看优化信息需要 诊断版 JVM 或性能分析工具;生产 JVM 不保证提供所有打印选项。

基准实验必须包含预热、稳定输入和结果消费,避免 JIT 把整段无用计算删除。微基准优先使用 JMH,不要用一个循环加 System.nanoTime() 推断分配成本。

下一步

阅读Class 文件、常量池与字节码指令,用 javap 把源码和 JVM 指令对应起来。


分享这篇文章:

上一篇
运行时数据区与 Java 内存模型不要混淆
下一篇
Class 文件、常量池与字节码指令