心得
对象实例数据与类型数据
假设有如下Java类定义:
java
public class Person {
String name;
int age;
}
当你在代码中写下:
java
Person p = new Person();
p.name = "Alice";
p.age = 30;
在JVM中,p是一个引用变量。假设采用“句柄访问”方式,JVM会在堆中为Person对象分配一块内存用于存储实例数据, 同时在句柄池中分配一块内存用于存储句柄。p变量中存储的是句柄的地址。
对象实例数据
这部分数据存储在堆内存的某个区域,内容包括该对象的所有实例字段的实际值。对于上面的例子,
p
对象的实例数据就是 name
字段的引用(指向字符串"Alice"的地址)和 age
字段的值(30)。每个 Person
对象的实例数据都是独立的。
类型数据
类型数据是描述 Person
类本身的信息,通常由JVM的“方法区”或“元空间”存储。它包括:
- 类的名称(如"Person")
- 父类信息(如
java.lang.Object
) - 字段表(如
name
是String
类型,age
是int
类型) - 方法表(如构造方法、
toString()
等) - 访问修饰符(如public、private等)
- 运行时常量池
- 其他元数据(如类加载器引用等)
句柄结构示意
txt
句柄地址
├─ 指向实例数据的指针(如0x1000,堆内存中p对象的实际数据)
└─ 指向类型数据的指针(如0x2000,方法区中Person类的元数据)
当你通过p.name访问字段时,JVM会先通过p找到句柄,再通过句柄中的实例数据指针找到实际的对象数据, 再根据类型数据指针找到字段的偏移量和类型信息,最终定位到name字段的值。
为什么实例数据存储在堆内存中
类型数据本身存放在方法区中?
这是因为它们的生命周期和访问模式决定的,实例数据属于每个对象独有,随着对象的创建和销毁而分配和回收,因此最适合存放在堆内存中, 堆是专门为对象实例的动态分配和垃圾回收设计的。而类型数据(即类的元数据)描述的是类的结构、方法、字段等信息,这些信息在类加载后 通常在整个JVM生命周期内保持不变,且被所有该类的对象共享,因此存放在方法区(或JDK8以后的元空间)这种专门用于存储类元数据的区域。