Wrayの知识库 Wrayの知识库
首页
  • Java 基础
  • Java 集合
  • Java 并发
  • Java IO
  • JVM
  • Spring Framework
  • Spring Boot
  • Spring Cloud
  • Spring Security
  • MySQL
  • Redis
  • 计算机基础
  • 操作系统原理
  • Linux
  • MacOS
  • Windows
  • 系统工程与研究专题
  • AI 基础
  • 大模型基础
  • Prompt 工程
  • RAG 检索增强生成
  • Agent 智能体
  • AI 应用开发
  • AI 工程化
  • AI 安全与治理
  • AI 面试与设计题
  • 纸质书
  • 电子书
  • 学习课程
疑难杂症
GitHub (opens new window)
首页
  • Java 基础
  • Java 集合
  • Java 并发
  • Java IO
  • JVM
  • Spring Framework
  • Spring Boot
  • Spring Cloud
  • Spring Security
  • MySQL
  • Redis
  • 计算机基础
  • 操作系统原理
  • Linux
  • MacOS
  • Windows
  • 系统工程与研究专题
  • AI 基础
  • 大模型基础
  • Prompt 工程
  • RAG 检索增强生成
  • Agent 智能体
  • AI 应用开发
  • AI 工程化
  • AI 安全与治理
  • AI 面试与设计题
  • 纸质书
  • 电子书
  • 学习课程
疑难杂症
GitHub (opens new window)
  • Java章节编写规范
  • Java基础

  • Java集合

  • Java并发

  • Java IO

  • JVM

    • JVM概述
    • JVM基础

    • 类加载机制

    • 运行时内存

      • Java堆
      • 方法区与元空间
      • 虚拟机栈本地方法栈与程序计数器
      • 对象创建与内存布局
        • 1. 对象创建过程
        • 2. 内存分配方式
        • 3. TLAB
        • 4. 对象内存布局
        • 5. 对象头
        • 6. 压缩指针
        • 7. 对象一定在堆上吗
        • 8. 阶段小结
        • 9. 对象创建详细流程图
        • 10. 对象布局示意
        • 11. 字段排列和内存占用
        • 12. 开发中常见问题
          • 12.1 为什么大量小对象也会占很多内存
          • 12.2 为什么 new 对象很快
        • 专家速查表
        • 13. Tips 快问快答
      • 直接内存
      • OOM分类与定位
    • 垃圾回收

    • 执行引擎与优化

    • 工具与问题排查

    • 调优实践与面试

目录

对象创建与内存布局

Java 中一句 new Object() 背后,JVM 做了类加载检查、内存分配、对象初始化等一系列动作。理解对象创建过程,有助于理解性能优化、锁、GC 和内存占用。

# 1. 对象创建过程

创建对象大致包括:

  1. 类加载检查。
  2. 分配内存。
  3. 初始化零值。
  4. 设置对象头。
  5. 执行构造方法。
User user = new User();

这行代码不是简单分配一块内存,还要保证类已经加载并完成必要初始化。

# 2. 内存分配方式

常见分配方式:

  • 指针碰撞。
  • 空闲列表。

如果堆内存规整,JVM 只需要移动指针即可分配对象,这就是指针碰撞。

如果堆内存不规整,JVM 需要维护可用内存块列表,从列表中找合适空间。

垃圾回收器是否整理内存,会影响分配方式。

# 3. TLAB

TLAB(Thread Local Allocation Buffer)是线程本地分配缓冲区。

多线程同时在堆上分配对象会产生竞争。JVM 会给每个线程在 Eden 中分配一小块私有区域,对象优先在线程自己的 TLAB 中分配。

优点:

  • 减少分配锁竞争。
  • 提升小对象分配效率。

TLAB 分配失败后,可能走慢路径分配。

# 4. 对象内存布局

HotSpot 中普通对象内存布局通常包括:

  • 对象头。
  • 实例数据。
  • 对齐填充。

数组对象还会包含数组长度。

对象 = 对象头 + 实例字段 + 对齐填充
数组 = 对象头 + 数组长度 + 数组元素 + 对齐填充

# 5. 对象头

对象头通常包含:

  • Mark Word。
  • Klass Pointer。

Mark Word 中可能存放:

  • 哈希码。
  • GC 年龄。
  • 锁状态。
  • 偏向线程信息。

Klass Pointer 指向类元数据,用于知道这个对象属于哪个类。

# 6. 压缩指针

64 位 JVM 中,对象引用默认可能使用压缩指针,以减少内存占用。

相关参数:

-XX:+UseCompressedOops
-XX:+UseCompressedClassPointers

压缩指针对大多数应用有利,可以降低对象引用和类指针占用。

# 7. 对象一定在堆上吗

从语义上看,对象分配在堆上。但经过 JIT 优化后,如果对象没有逃逸,可能被标量替换,甚至不真正分配对象。

例如:

public int sum() {
    Point p = new Point(1, 2);
    return p.x + p.y;
}

如果 p 没有逃逸出方法,JIT 可能把对象拆成两个局部变量。

# 8. 阶段小结

对象创建涉及类加载、内存分配、零值初始化、对象头设置和构造方法执行。对象布局中的对象头、实例数据、对齐填充会影响内存占用。理解 TLAB 和逃逸分析,有助于理解 JVM 为什么能高效分配大量短生命周期对象。

# 9. 对象创建详细流程图

执行 new 指令
  │
  ├─ 1. 检查类是否已加载
  │      └─ 未加载则触发类加载
  │
  ├─ 2. 计算对象所需内存大小
  │
  ├─ 3. 尝试在 TLAB 中分配
  │      ├─ 成功 -> 继续
  │      └─ 失败 -> 进入慢路径分配
  │
  ├─ 4. 初始化内存为零值
  │
  ├─ 5. 设置对象头
  │      ├─ Mark Word
  │      └─ Klass Pointer
  │
  ├─ 6. 执行 <init> 构造方法
  │
  └─ 7. 引用指向新对象

这里的“零值初始化”很关键,它保证 Java 字段即使没有显式赋值,也有确定默认值。

# 10. 对象布局示意

普通对象
┌─────────────────────┐
│ Mark Word            │  hash、GC 年龄、锁状态等
├─────────────────────┤
│ Klass Pointer        │  指向类元数据
├─────────────────────┤
│ 实例字段              │  int、long、引用字段等
├─────────────────────┤
│ 对齐填充              │  对齐到对象粒度
└─────────────────────┘

数组对象
┌─────────────────────┐
│ Mark Word            │
├─────────────────────┤
│ Klass Pointer        │
├─────────────────────┤
│ Array Length         │
├─────────────────────┤
│ 数组元素              │
├─────────────────────┤
│ 对齐填充              │
└─────────────────────┘

对象对齐意味着一个对象的实际占用可能大于字段大小之和。

# 11. 字段排列和内存占用

JVM 可能对字段布局做优化,以减少对齐填充。但开发中仍要知道:

  • long、double 通常占 8 字节。
  • 引用在开启压缩指针时通常占 4 字节。
  • 对象头本身有固定开销。
  • 大量小对象的对象头开销非常可观。

例如一个只包含一个 boolean 字段的对象,也不可能只占 1 字节。

# 12. 开发中常见问题

# 12.1 为什么大量小对象也会占很多内存

每个对象都有对象头和对齐填充。大量小对象的元数据开销可能超过字段本身。

优化方向:

  • 使用基本类型数组。
  • 合并对象。
  • 避免过度拆分模型。
  • 使用对象池要谨慎,现代 GC 对短命对象很擅长。

# 12.2 为什么 new 对象很快

大多数对象在 TLAB 中分配,通常只是移动指针,成本很低。真正昂贵的往往不是 new 本身,而是对象逃逸、长期存活、GC 压力和缓存未命中。

# 专家速查表

组成部分 内容 优化关注点
对象头 Mark Word hash、锁状态、GC 年龄等 锁优化和对象状态
类型指针 指向 Klass 元数据 压缩类指针影响大小
实例数据 对象字段值 字段类型和排列影响占用
对齐填充 8 字节对齐补齐 小对象数量多时影响明显
引用字段 指向其他对象 对象图深度影响 GC

对象大小不是字段大小简单相加,还受到对象头、引用压缩、字段排列和对齐影响。大量小对象场景中,减少对象数量往往比微调字段更有效。

# 13. Tips 快问快答

Q:对象头里一定有 hashCode 吗?

A:对象头 Mark Word 可以存 hashCode,但通常在真正调用 hashCode() 后才需要保存相关信息。

Q:数组对象为什么比普通对象多一块长度信息?

A:JVM 需要知道数组长度以支持边界检查和 array.length。

Q:开启压缩指针有什么好处?

A:减少引用字段占用,降低堆内存压力,提高缓存友好性。

Q:对象分配失败一定立刻 OOM 吗?

A:不一定。JVM 可能先触发 GC,GC 后仍无法分配才可能 OOM。

上次更新: 2026/06/25, 14:19:18
虚拟机栈本地方法栈与程序计数器
直接内存

← 虚拟机栈本地方法栈与程序计数器 直接内存→

Copyright © 2023-2026 Wray | 鄂ICP备2024050235号-1
  • 跟随系统
  • 浅色模式
  • 深色模式
  • 阅读模式