Wrayの知识库 Wrayの知识库
首页
  • Java 基础
  • Java 集合
  • Java 并发
  • Java IO
  • JVM
  • Spring Framework
  • Spring Boot
  • Spring Cloud
  • Spring Security
  • MySQL
  • Redis
  • 计算机基础
  • 操作系统原理
  • Linux
  • MacOS
  • Windows
  • 系统工程与研究专题
  • AI 基础
  • 大模型基础
  • Prompt 工程
  • RAG 检索增强生成
  • Agent 智能体
  • AI 应用开发
  • AI 工程化
  • AI 安全与治理
  • AI 面试与设计题
  • 纸质书
  • 电子书
  • 学习课程
疑难杂症
GitHub (opens new window)
首页
  • Java 基础
  • Java 集合
  • Java 并发
  • Java IO
  • JVM
  • Spring Framework
  • Spring Boot
  • Spring Cloud
  • Spring Security
  • MySQL
  • Redis
  • 计算机基础
  • 操作系统原理
  • Linux
  • MacOS
  • Windows
  • 系统工程与研究专题
  • AI 基础
  • 大模型基础
  • Prompt 工程
  • RAG 检索增强生成
  • Agent 智能体
  • AI 应用开发
  • AI 工程化
  • AI 安全与治理
  • AI 面试与设计题
  • 纸质书
  • 电子书
  • 学习课程
疑难杂症
GitHub (opens new window)
  • Java章节编写规范
  • Java基础

  • Java集合

  • Java并发

  • Java IO

  • JVM

    • JVM概述
    • JVM基础

    • 类加载机制

    • 运行时内存

      • Java堆
      • 方法区与元空间
        • 1. 方法区存什么
        • 2. 永久代
        • 3. 元空间
        • 4. 为什么移除永久代
        • 5. 运行时常量池
        • 6. 字符串常量池
        • 7. 元空间问题排查
        • 8. 阶段小结
        • 9. 类元数据进入元空间的流程
        • 10. 元空间增长的常见来源
        • 11. 类加载器泄漏图解
        • 12. Tips 快问快答
      • 虚拟机栈本地方法栈与程序计数器
      • 对象创建与内存布局
      • 直接内存
      • OOM分类与定位
    • 垃圾回收

    • 执行引擎与优化

    • 工具与问题排查

    • 调优实践与面试

目录

方法区与元空间

方法区用于存储类相关信息。它是 JVM 规范中的概念,HotSpot 在不同 JDK 版本中有不同实现:JDK 8 之前主要是永久代,JDK 8 之后使用元空间。

# 1. 方法区存什么

方法区主要存储:

  • 类的元数据。
  • 字段信息。
  • 方法信息。
  • 常量信息。
  • 静态变量相关数据。
  • 运行时常量池。

它服务于类加载和方法执行。

# 2. 永久代

在 JDK 8 之前,HotSpot 使用永久代实现方法区。

永久代使用 JVM 堆内或受 JVM 管理的一块内存,大小通常通过参数控制:

-XX:PermSize
-XX:MaxPermSize

永久代常见问题:

java.lang.OutOfMemoryError: PermGen space

常见于大量类加载、动态代理、热部署和应用服务器类加载器泄漏。

# 3. 元空间

JDK 8 移除了永久代,改用元空间实现方法区。

元空间使用本地内存,常见参数:

-XX:MetaspaceSize=256m
-XX:MaxMetaspaceSize=512m

如果不限制最大值,元空间可能受可用本地内存限制。

元空间不足可能出现:

java.lang.OutOfMemoryError: Metaspace

# 4. 为什么移除永久代

永久代大小较难估计,容易因为类元数据增长导致 OOM。元空间使用本地内存,可以更灵活地管理类元数据。

但这不代表元空间不会 OOM。大量动态生成类、类加载器泄漏仍然会耗尽元空间。

# 5. 运行时常量池

运行时常量池是方法区的一部分,用于存放编译期生成的各种字面量和符号引用。

例如:

  • 字符串字面量。
  • 类和方法符号引用。
  • 字段符号引用。

类加载后,Class 文件常量池会进入运行时常量池。

# 6. 字符串常量池

字符串常量池和运行时常量池相关,但在 HotSpot 中其具体位置随版本变化。

JDK 7 之后,字符串常量池移到了堆中。

String a = "hello";
String b = "hello";
System.out.println(a == b); // true

字面量字符串会复用常量池中的对象。

# 7. 元空间问题排查

排查方向:

  • 是否大量动态生成类。
  • 是否频繁创建新的类加载器。
  • 是否存在热部署未释放。
  • 是否大量使用 CGLIB、ByteBuddy、Javassist。
  • 是否有脚本引擎或规则引擎动态编译。

可以结合类加载日志、jcmd VM.classloader_stats、堆转储分析。

# 8. 阶段小结

方法区是规范概念,永久代和元空间是 HotSpot 的实现。JDK 8 后,元空间取代永久代,但类元数据依然需要内存。类加载器泄漏和动态生成类仍然可能导致元空间 OOM。

# 9. 类元数据进入元空间的流程

ClassLoader 读取 class 字节流
        │
        ▼
验证、准备、解析
        │
        ▼
在元空间保存类元数据
        │
        ├─ 类名、父类、接口
        ├─ 字段表
        ├─ 方法表
        ├─ 常量池运行时结构
        └─ 注解、泛型签名等属性
        │
        ▼
堆中生成 java.lang.Class 对象

注意:Class 对象在堆中,类元数据主要在元空间中。二者有关联,但不是同一个东西。

# 10. 元空间增长的常见来源

来源 说明
应用启动加载大量类 正常增长
Spring/CGLIB 动态代理 生成代理类
MyBatis、Hibernate 等框架 可能生成辅助类或代理类
Groovy、Janino、规则引擎 动态编译类
热部署 新类加载器加载新版本类
插件系统 每个插件可能有独立类加载器

正常应用启动后,类加载数量应该趋于稳定。如果运行期间类数量持续增长,就要怀疑动态生成类或类加载器泄漏。

# 11. 类加载器泄漏图解

GC Roots
  │
  ▼
某个静态变量 / 线程 / ThreadLocal / 注册表
  │
  ▼
旧 WebAppClassLoader
  │
  ▼
它加载的所有 Class
  │
  ▼
元空间中的类元数据无法释放

热部署场景中,如果旧类加载器没有释放,旧版本应用的类元数据会一直留在元空间。

# 12. Tips 快问快答

Q:元空间在堆里吗?

A:HotSpot JDK 8+ 中元空间使用本地内存,不在 Java 堆里。

Q:MaxMetaspaceSize 不设置可以吗?

A:可以,但生产环境建议结合容器总内存设置上限,避免元空间无限增长导致进程内存压力。

Q:字符串常量池在元空间吗?

A:HotSpot JDK 7 之后字符串常量池主要在堆中,Class 文件常量池运行时结构和方法区相关。

Q:元空间 OOM 怎么排查?

A:看类加载数量、类加载器数量、动态代理/脚本生成、热部署和 jcmd VM.classloader_stats。

上次更新: 2026/06/24, 16:03:01
Java堆
虚拟机栈本地方法栈与程序计数器

← Java堆 虚拟机栈本地方法栈与程序计数器→

Copyright © 2023-2026 Wray | 鄂ICP备2024050235号-1
  • 跟随系统
  • 浅色模式
  • 深色模式
  • 阅读模式