Java学习-面向对象02从内存分析对象的创建(了解)
从内存分析对象的创建(了解)
Java中有三大重要的内存区域,之前讲过,这里不赘述。
Java对象的创建在内存中的具体实现是一个非常重要的底层机制,涉及 JVM(Java Virtual Machine)运行时数据区、类加载、内存分配等多个方面。我可以详细描述整个流程,并配合文字结构图示来帮助看文章的你理解。
一、Java对象创建的整体流程
当你使用如下代码创建一个 Java 对象:
Person p = new Person();
JVM 内部会经历以下几个关键步骤:
1. 类加载检查(Class Loading)
- JVM 首先检查
new Person()中的符号引用是否能在常量池中定位到一个类的符号引用。 - 然后检查这个类是否已经被加载、解析和初始化过。
- 如果没有,则触发类加载过程(ClassLoader 加载该类)。
2. 分配内存(Memory Allocation)
- 对象所需的内存大小在类加载完成后就可以确定。
- JVM 需要从堆(Heap)中为对象分配一块连续的空间。
内存分配方式:
- 指针碰撞(Bump the Pointer):适用于内存规整的情况(如 Serial、ParNew 收集器)。
- 空闲列表(Free List):适用于内存不连续的情况(如 CMS 收集器)。
并发安全问题:多个线程同时分配内存时,需要保证操作是原子性的,通常通过 TLAB(Thread Local Allocation Buffer)解决。
3. 初始化零值(Zero Value Initialization)
- 分配内存后,JVM 会将对象的内存空间初始化为零值(如 int 为 0,boolean 为 false)。
4. 设置对象头(Object Header)
- 每个对象都有一个对象头(Object Header),用于存储以下信息:
- Mark Word:哈希码、GC 分代年龄、锁状态标志等。
- Klass Pointer:指向类元数据的指针(即类型信息)。
- 数组长度(如果是数组)
在 64 位 JVM 中,对象头通常是 16 字节(Mark Word 8 字节 + Klass Pointer 8 字节)。
5. 执行构造函数(Constructor Call)
- 最后,JVM 调用
<init>方法(即构造函数),执行用户定义的初始化逻辑。
二、Java 堆内存布局与对象结构图解(文字版)
我们用文字模拟一张图来展示对象在堆中的内存布局。
|-----------------------------|
| Object Header | ← Mark Word (8 bytes) + Klass Pointer (8 bytes)
|-----------------------------|
| Instance Fields |
| (int age, String name) | ← 根据类定义排列字段
|-----------------------------|
| Padding | ← 可选填充字节,对齐到8字节边界
|-----------------------------|
示例说明:
class Person {
private int age;
private String name;
}
那么对象实例的内存布局大致如下:
[Object Header] [age (4B)] [name (8B)] [Padding...]
三、JVM 运行时数据区简图(文字模拟)
+----------------------------+
| Method Area | ← 存储类信息、常量池、静态变量等
+----------------------------+
| Heap (堆) | ← 存放对象实例(Person 实例)
+----------------------------+
| Java Stack | ← 每个线程私有栈,保存局部变量表(如 Person p)
+----------------------------+
| PC Register | ← 当前线程执行的字节码行号指示器
+----------------------------+
| Native Method Stack | ← 调用本地方法(JNI)使用的栈
+----------------------------+
四、TLAB(线程本地分配缓冲区)
由于多线程并发分配内存可能导致冲突,JVM 引入了 TLAB(Thread Local Allocation Buffer):
- 每个线程预先在堆中申请一小块内存作为自己的专属区域。
- 对象优先在 TLAB 中分配。
- TLAB 空间不足时,才会同步锁定并从 Eden 区分配。
五、总结图示(流程图结构)
new Person()
↓
[类加载检查] → 是否已加载?→ 否 → 类加载子系统加载类
↓ 是
[分配内存] → 使用指针碰撞或空闲列表 → TLAB?
↓
[初始化零值]
↓
[设置对象头(Mark Word + Klass Pointer)]
↓
[调用构造函数 <init>]
↓
[对象创建完成]
六、补充说明
| 组件 | 描述 |
|---|---|
| JVM 堆 | 所有线程共享,存放对象实例 |
| Eden 区 | 新生对象主要分配在此区域 |
| Survivor 区 | GC 中存活的对象会进入这里 |
| Old 区 | 长期存活的对象 |
| TLAB | 线程私有内存区域,提升对象分配效率 |
| Mark Word | 存储对象的哈希码、锁状态、GC 年龄等信息 |
浙公网安备 33010602011771号