源码中的 new 不等于一次昂贵的系统内存申请。HotSpot 通常在线程本地分配缓冲区中移动指针完成小对象分配,JIT 还
可能消除未逃逸对象。性能判断必须以分配采样和基准结果为证据。
Table of contents
Open Table of contents
一次对象创建的逻辑步骤
执行 new 字节码时,JVM 先确保目标类已加载并初始化,再为实例分配空间、把字段设为默认值、写入对象头,最后调用
构造器 <init>。
对象通常包含三部分:
- 对象头:保存锁状态、哈希等运行时标记,以及类型信息。
- 实例数据:保存字段值。
- 对齐填充:让对象大小满足 JVM 的对齐要求。
这些是 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 指令对应起来。