Java实例对象在堆内存和方法区中存储的内容(实例详解)
一、实例对象在堆内存中存储的内容
堆内存负责存储对象的实例数据和运行时状态,具体包含以下信息:
1. 对象头(Object Header)
- Mark Word(标记字段):存储对象自身的运行时数据,包括:
- 哈希码(Hash Code)
- 分代年龄(GC 年龄)
- 锁状态(偏向锁、轻量级锁、重量级锁)
- 线程持有的锁信息
- 是否被 GC 标记为可回收等
- 类型指针(Klass Pointer):指向方法区中该类的元数据(即类的结构信息),JVM 通过此指针确定对象属于哪个类。
- 数组长度(仅数组对象):如果对象是数组,对象头中会额外存储数组的长度。
2. 实例数据(Instance Data)
- 对象的所有成员变量(包括继承自父类的成员变量)。
- 基本类型字段直接存储值(如
int age = 25)。 - 引用类型字段存储指向其他对象的指针(如
String name指向堆中的String对象)。
3. 对齐填充(Padding)
- JVM 要求对象大小必须是 8 字节的整数倍。对齐填充用于补齐对象头+实例数据后不足的部分,以提高内存访问效率。
二、方法区中存储的类元数据
方法区(JDK8+ 称为 元空间)存储类的结构信息,与具体实例无关,供所有实例共享。包括:
1. 类元数据(Class Metadata)
- 类的全限定名、访问修饰符(
public/final等)、父类名、实现的接口列表。 - 字段表(Field Table):所有字段的名称、类型、修饰符、偏移量等。
- 方法表(Method Table):所有方法的名称、参数列表、返回类型、字节码指令、异常表等。
- 运行时常量池(Runtime Constant Pool):编译期生成的字面量、符号引用,以及运行期解析后的直接引用。
2. 静态变量(Static Variables)
- 类的静态变量(
static修饰的字段)存储在方法区中。 - 注意:静态变量本身在方法区,但如果静态变量是引用类型(如
static Object obj),其指向的对象实例仍在堆中。
3. 方法字节码(Method Bytecode)
- 类中所有方法的编译后的字节码指令。
- JIT 编译后的本地机器码(缓存优化后的代码)。
4. 类加载器引用(ClassLoader Reference)
- 指向加载该类的类加载器的引用,用于类卸载时判断是否可达。
三、堆与方法区的协作关系
通过 new 创建对象时,JVM 会按以下流程协作堆和方法区:
- 定位类元数据
- JVM 根据
new的类名,在方法区中查找该类的元数据。若未加载,则触发类加载过程。
- JVM 根据
- 分配堆内存
- 根据方法区中记录的类结构信息(如对象大小),在堆中划分一块内存空间。
- 初始化对象头
- 填充 Mark Word(初始哈希码、GC 年龄等)和类型指针(指向方法区的类元数据)。
- 设置实例数据
- 根据方法区中字段表的偏移量,将成员变量初始化为默认值(如
int初始化为 0,引用初始化为null)。
- 根据方法区中字段表的偏移量,将成员变量初始化为默认值(如
- 执行构造函数
- 从方法区中获取
<init>方法的字节码,执行构造函数代码,完成对象初始化。
- 从方法区中获取
四、实例对象与类元数据的交互示例
public class Person {
private String name; // 实例字段(堆中存储)
private int age; // 实例字段(堆中存储)
public static int count; // 静态字段(方法区存储)
public Person(String name, int age) {
this.name = name;
this.age = age;
}
public void sayHello() {
System.out.println("Hello!"); // 方法字节码(方法区存储)
}
}
// 创建对象
Person p = new Person("Alice", 30);内存分配示意图
堆内存(Heap) 方法区(Metaspace) +------------------+ +-----------------------+ | Person 实例对象 | | Person 类元数据 | | - 对象头 | <---类型指针---- | - 字段表(name, age) | | - Mark Word | | - 方法表(sayHello) | | - Klass Pointer| | - 运行时常量池 | | - 实例数据 | | - 静态变量 count | | - name → String| +-----------------------+ | - age = 30 | +------------------+
五、关键总结
- 堆内存:存储实例对象的运行时数据(对象头、实例字段值、对齐填充)。
- 方法区:存储类的结构信息(字段表、方法表、运行时常量池等),以及静态变量。
- 协作机制:堆中的对象通过类型指针(Klass Pointer)关联方法区的类元数据,实现对象行为(方法调用、类型检查等)。
基本数据类型属性值的存放位置
static 修饰的属性存放在方法区(元空间),final 修饰的属性的存储位置取决于是否同时被 static 修饰,非静态 final 属性存放在堆中对象的内存空间里,静态 final 属性存放在方法区(元空间)。
当对象的属性是基本数据类型时,其值直接存放在堆中对象的内存空间里。
示例代码:
class Person {
// 基本数据类型属性
int age;
double height;
}
public class Main {
public static void main(String[] args) {
// 创建 Person 对象
Person person = new Person();
person.age = 20;
person.height = 1.75;
}
}解释:
在上述代码中,Person 类有两个基本数据类型的属性 age 和 height。当创建 Person 对象时,会在堆中为该对象分配一块内存空间,age 和 height 的值会直接存放在这块内存空间里。
引用数据类型属性值的存放位置
当对象的属性是引用数据类型时,该属性在对象的内存空间中存储的是所引用对象在堆中的地址,而实际的对象数据存放在堆的其他位置。
示例代码:
class Address {
String city;
}
class Person {
// 引用数据类型属性
Address address;
}
public class Main {
public static void main(String[] args) {
// 创建 Address 对象
Address address = new Address();
address.city = "Beijing";
// 创建 Person 对象
Person person = new Person();
person.address = address;
}
}解释:
在上述代码中,Person 类有一个引用数据类型的属性 address。当创建 Person 对象时,在堆中为 Person 对象分配内存空间,address 属性在该内存空间中存储的是 Address 对象在堆中的地址。而 Address 对象本身也在堆中占用一块内存空间,其属性 city 的值存放在这块内存空间里。
综上所述,Java 创建实例对象时,基本数据类型的属性值直接存放在堆中对象的内存空间里,引用数据类型的属性在对象的内存空间中存储的是所引用对象在堆中的地址,实际对象数据存放在堆的其他位置。
到此这篇关于Java实例对象在堆内存和方法区中存储的内容(实例详解)的文章就介绍到这了,更多相关java对象存储在堆内存内容请搜索脚本之家以前的文章或继续浏览下面的相关文章希望大家以后多多支持脚本之家!
相关文章
idea运行tomcat报错找不到catalina.bat,系统找不到指定的文件问题
这篇文章主要介绍了idea运行tomcat报错找不到catalina.bat,系统找不到指定的文件问题,具有很好的参考价值,希望对大家有所帮助,2023-11-11
如何解决Maven打包时每次都出现Download maven-metadata.xml卡住问题
这篇文章主要介绍了如何解决Maven打包时每次都出现Download maven-metadata.xml卡住问题,具有很好的参考价值,希望对大家有所帮助,如有错误或未考虑完全的地方,望不吝赐教2024-05-05
为什么ConcurrentHashMap的key value不能为null,map可以?
这篇文章主要介绍了为什么ConcurrentHashMap的key value不能为null,map可以呢?具有很好的参考价值,希望对大家有所帮助。一起跟随小编过来看看吧2021-01-01
IntelliJ IDEA 2021.3永久最新激活至2099年(亲测有效)
最新版idea2021.3已出来,很多网友迫不及待的要升级idea2021最新版,今天小编抽空给大家整理了一篇教程关于idea2021.3最新激活教程,本文以idea2021.2.3为例通过图文并茂的形式给大家分享激活详细过程,感兴趣的朋友参考下吧2020-12-12


最新评论