JVM--方法区(Method Area)

JVM–方法区(Method Area)

背景

  • JVM–方法区(Method Area)

  • 博主以黑马JVM进行学习

定义

  • 方法区是 JVM 中线程共享的内存区域,用于存储已被虚拟机加载的类元数据(类的结构信息)、常量、静态变量、即时编译器编译后的代码缓存等;
  • 补充:方法区是 JVM 规范中的逻辑概念,不同 JVM 实现有不同的物理载体(1.6 用永久代,1.8 用元空间)。

组成

  • JVM内存结构1.6

    • 方法区(概念):PermGen永久代(实现)包括:常量池(StringTable)、Class、ClassLoader
  • JVM内存结构1.8

    • 方法区(概念):Metaspace元空间(实现)(包括:常量池、Class、ClassLoader)(由本地内存管理),堆(包含StringTable)
  • 比较

    JVM 版本 方法区的实现方式 核心组成 关键差异
    JDK 1.6 永久代(PermGen,属于堆内存的一部分) 1. 类元数据(Class 结构、字段 / 方法信息)2. 运行时常量池(包含 StringTable)3. 静态变量4. ClassLoader 相关信息 永久代受 JVM 堆内存限制,可通过 -XX:MaxPermSize 限制大小
    JDK 1.8 元空间(Metaspace,属于本地内存 / 直接内存) 1. 类元数据、静态变量、ClassLoader 信息(存储在元空间)2. 运行时常量池(仍属于方法区逻辑范畴)3. StringTable(移至堆内存) 元空间默认使用本地内存(不受 JVM 堆限制),可通过 -XX:MaxMetaspaceSize 限制

方法区的内存溢出

  • 1.8以前会导致永久代内存溢出(OutOfMemoryError: PermGen space)

    • -XX:MaxPermSize=内存大小
  • 1.8之后会导致元空间内存溢出(OutOfMemoryError: Metaspace)

    • 元空间大小根据物理内存大小

    • -MaxMetaspaceSize=内存大小

  • 比较

    版本 溢出错误 核心参数 参数说明
    JDK 1.6- OutOfMemoryError: PermGen space -XX:PermSize(初始永久代大小)-XX:MaxPermSize(最大永久代大小) 默认值较小(比如 64M),超出则溢出
    JDK 1.8+ OutOfMemoryError: Metaspace -XX:MetaspaceSize(元空间初始阈值,触发 GC 的阈值)-XX:MaxMetaspaceSize(元空间最大大小) 默认无上限(耗尽本地内存才溢出),建议显式设置上限
  • 场景:

    核心原因:动态生成大量类,导致类元数据占满方法区

    • spring:通过 CGLIB/JDK 动态代理生成大量代理类;
    • mybatis:Mapper 接口动态生成实现类;
    • 其他:反射、动态字节码生成(ASM/ClassWriter)、热部署频繁加载类;

运行时常量池(Constant pool)

  • 常量池,就是一张表,虚拟机指令根据这张常量表找到要执行的类名、方法名、参数类型、字面量等信息

  • 运行时常量池,常量池是*.class文件中的,当该类被加载,它的常量池信息就会放入运行时常量池,并把里面的符号地址变为真实地址

  • .class 文件的核心组成:

  • 二进制字节码 = 类基本信息 + 常量池 + 类方法定义(含虚拟机指令) + 字段定义

  • 虚拟机指令通过常量池的索引查找数据:比如指令 ldc #1 表示加载常量池第 1 项的字面量;

  • JDK中的javap -v反编译后显示详细信息

总结

  • 方法区是 JVM 线程共享的逻辑区域,JDK 1.6 用永久代(堆内)实现,JDK 1.8 改用元空间(本地内存)实现,核心变化是 StringTable 移至堆、元空间不受堆内存限制;
  • 方法区溢出在 1.6 表现为 PermGen space,1.8 表现为 Metaspace,核心场景是动态生成大量类(Spring/MyBatis 代理),可通过 -XX:MaxPermSize/-XX:MaxMetaspaceSize 限制大小;
  • 运行时常量池是类加载后常量池的运行时形态,存储字面量和符号引用(加载后转为直接引用),具备动态性,是虚拟机指令执行的核心数据来源。

补充

  • 元空间不是堆内存,而是本地内存,默认无上限,需手动设置 -XX:MaxMetaspaceSize 防止耗尽系统内存;
  • StringTable(字符串常量池)在 1.8 移至堆,因此字符串相关的内存溢出是 Java heap space,而非元空间溢出;
  • 运行时常量池≠字符串常量池:前者包含后者(1.6),1.8 后字符串常量池独立在堆中,运行时常量池仍在元空间(逻辑上属于方法区)。