Java元空间存储类的运行时常量池、字段与方法元数据、Klass结构、ClassLoader相关内部对象及JIT编译元信息;其内存仅在类卸载时回收,且JIT编译可能引发内存碎片。

Java元空间用于存放JVM加载的类的元数据信息,取代了永久代(PermGen)的功能。以下是元空间中实际存储的JVM内部数据结构及其说明:
一、类的运行时常量池
元空间存储每个已加载类的运行时常量池,包含编译期生成的各种字面量(如字符串字面量、final修饰的基本类型常量)和符号引用(如类和接口的全限定名、字段和方法的名称与描述符)。该常量池在类加载阶段被创建并存入元空间,供运行时解析使用。
1、字符串字面量如"Hello"在首次使用时被放入元空间中的字符串常量池(JDK 7起移至堆,但JDK 8+中仍存在部分元空间关联结构)。
2、符号引用在类加载的解析阶段被转换为直接引用前,始终以原始形式保留在元空间的常量池中。
立即学习“Java免费学习笔记(深入)”;
3、动态生成的常量池项(如通过invokedynamic指令触发的BootstrapMethods属性内容)也分配于元空间。
二、类的字段和方法的元数据
每个类的字段(Field)和方法(Method)的结构化描述信息均驻留于元空间。这些信息不包含实际字段值或方法字节码执行体,而是JVM内部用于快速定位、校验和分派的数据结构。
1、字段元数据包括字段名、描述符、访问标志(如public、static)、签名(Signature)以及对应在类实例或静态存储区中的偏移量。
2、方法元数据包括方法名、描述符、访问标志、参数数量、局部变量表大小、操作数栈深度、异常表入口,以及指向其字节码所在内存块的指针(该指针指向元空间内Method对象所管理的Code属性数据区)。
3、所有方法的注解(Annotation)信息,包括运行时保留的@Retention(RetentionPolicy.RUNTIME)注解,均以结构化形式序列化后存入元空间。
三、类的结构定义与继承关系信息
元空间保存每个已加载Class对象对应的C++层面的Klass结构(如InstanceKlass、ArrayKlass),其中封装了类的完整类型语义:超类、实现的接口列表、内部类关系、是否为匿名类或Lambda生成类等。这些结构支撑JVM的类型检查、instanceof判断、强制转换及反射调用。
1、InstanceKlass中维护着指向其父类Klass和接口Klass数组的指针,全部位于元空间内。
2、类的vtable(虚函数表)和itable(接口方法表)作为紧凑数组结构,直接分配于元空间,由Klass对象持有其起始地址。
3、对于通过Unsafe.defineAnonymousClass或LambdaMetafactory生成的匿名类,其Klass结构及关联的常量池同样完全驻留于元空间,且不可被卸载(除非其定义类被卸载)。
四、方法区相关的内部对象
JVM自身在运行过程中创建的部分关键内部对象,例如java.lang.Class实例的镜像(即OopDesc在HotSpot中对应的_klass字段所指向的Klass对象)、ClassLoader对象持有的已定义类列表节点、以及与类加载过程强相关的ProtectionDomain、SignerEntry等安全上下文结构,均分配在元空间中。
1、每个加载到JVM中的Class对象,在Java堆中仅保存一个轻量级引用,其核心类型信息(如_is_interface、_is_hidden、_init_state)全部由元空间中的Klass实例承载。
2、BootstrapClassLoader、PlatformClassLoader和SystemClassLoader各自维护的已定义类缓存(如classes列表),其节点结构(如ClassLoaderData中的loaded_classes链表节点)本身分配于元空间。
3、类卸载的前提是其对应的ClassLoader对象不可达且该类无任何活动实例——此时JVM才会回收其Klass结构、常量池及所有关联元数据所占用的元空间内存。
五、JIT编译器生成的元信息
当JIT编译器(如C1/C2)对某个方法完成编译后,除生成本地机器码外,还需在元空间中注册相关元信息,以支持去优化(deoptimization)、调试、栈帧解析及性能监控。
1、nmethod结构体(包含入口地址、oop映射、调试信息、异常处理表指针等)整体分配于元空间的code cache区域(虽逻辑上独立,但HotSpot将其视为元空间子系统的一部分)。
2、编译后的代码所依赖的元数据(如Method*指针、Klass*指针、常量池索引映射表)均以只读方式绑定至该nmethod,并驻留于元空间。
3、JIT编译产物不会导致元空间OOM,但频繁的编译/去优化循环可能加剧元空间内存碎片,影响后续大块连续内存分配。










