方法区、永久代、元空间的关系

方法区

方法区与 Java 堆一样,是各个线程共享的内存区域,它用于存储已被虚拟机加载的类信息常量静态变量即时编译器编译后的代码等数据。虽然 Java 虚拟机规范把方法区描述为堆的一个逻辑部分,但是它却有一个别名叫做 Non-Heap(非堆),目的应该是与 Java 堆区分开来。

永久代(PermGen)

《Java 虚拟机规范》只是规定了有方法区这么个概念和它的作用,并没有规定如何去实现它。那么,在不同的 JVM 上方法区的实现肯定是不同的了。

方法区和永久代的关系很像 Java 中接口和类的关系,类实现了接口,而永久代就是 HotSpot 虚拟机对虚拟机规范中方法区的一种实现方式。也就是说,永久代是 HotSpot 的概念,方法区是 Java 虚拟机规范中的定义,是一种规范,而永久代是一种实现,一个是标准一个是实现,其他的虚拟机实现并没有永久代这一说法。

元空间(MetaSpace)

在 Java 官方的 HotSpot 虚拟机中,Java8 版本以后,是用元空间来实现的方法区;在Java8之前的版本,则是用永久代实现的方法区。

方法区大小常用参数

JDK 1.8 之前永久代还没被彻底移除的时候,通常通过下面这些参数来调节方法区大小:

  • -XX:PermSize=N 方法区 (永久代) 初始大小
  • -XX:MaxPermSize=N 方法区 (永久代) 最大值

超过这个值将会抛出 OutOfMemoryError 异常,即 java.lang.OutOfMemoryError: PermGen

相对而言,垃圾收集行为是很少出现在这个区域的,但并非数据进入方法区后就「永久存在」了。

JDK 1.8 的时候,方法区(HotSpot 的永久代)被彻底移除了(JDK1.7 就已经开始了),取而代之是元空间,元空间使用的是直接内存

  • -XX:MetaspaceSize=N 设置 Metaspace 的初始值(和最小大小),如果未指定此标志,则 Metaspace 将根据运行时的应用程序需求动态地重新调整大小。
  • -XX:MaxMetaspaceSize=N 设置 Metaspace 的最大值,默认值为 unlimited,这意味着它只受系统内存的限制。

当元空间溢出时会得到如下错误: java.lang.OutOfMemoryError: MetaSpace

与永久代很大的不同就是,如果不指定大小的话,随着更多类的创建,虚拟机会耗尽所有可用的系统内存。

为什么将永久代替换为元空间?

永久代有一个 JVM 本身设置固定大小上限,无法进行调整,而元空间使用的是直接内存,受本机可用内存的限制,虽然元空间仍旧可能溢出,但是比原来出现的几率会更小。

  1. 元空间里面存放的是类的元数据,这样加载多少类的元数据就不由 MaxPermSize 控制了, 而由系统的实际可用空间来控制,这样能加载的类就更多。
  2. 在 JDK1.8,合并 HotSpot 和 JRockit 的代码时,JRockit 从来没有一个叫永久代的东西,合并之后就没有必要额外地设置这么一个永久代的地方了。
<think>嗯,用户之前问过常量池和运行时常量池的关系,现在又问了方法区空间关系。看来他对Java内存域特别是方法区这部分比较感兴趣,或者在学习过程中遇到了相关的问题。 首先,我需要确认用户的问题点。方法区空间这两个概念在Java的不同版本中有变化,尤其是在JDK 8的时候,空间永久。用户可能在学习时发现资料中提到的方法区空间有所混淆,想要明确两者的关系别。 用户之前的问题是关于常量池的存储位置,涉及到方法区,所以现在这个问题可能是对之前内容的延伸。需要理清方法区JVM规范中的概念,而永久空间是具体的实现。不同版本的JDK实现方式不同,比如JDK7之前方法区永久实现,而JDK8之后改为空间,使用本地内存。 接下来,要考虑用户的背景。可能他正在准备面试或者学习JVM内存模型,遇到了这两个术语,需要明确它们的关系。这时候需要从规范和实现两个层面来解释,方法区是规范,空间是实现方式之一,取永久。 还要注意用户可能的误解,比如认为方法区空间是同一个东西,或者不清楚为何要做这样的改变。需要解释永久的问题,比如容易OOM,调优困难,而空间使用本地内存,可以自动扩展,减少OOM风险,提高性能。 另外,用户可能想知道空间的具体细节,比如存储的内容、内存管理方式(Metaspace和Compressed Class Space),以及相关的JVM参数。这些内容在回答中需要简要提及,但不宜过于深入,以免让用户感到混乱。 最后,总结两者的关系,强调方法区是逻辑概念,空间是具体实现,帮助用户形成清晰的理解。同时,联系用户之前的问题,说明运行时常量池现在位于空间,而字符串常量池在堆中,避免混淆。</think>### **用「仓库改造」比喻秒懂方法区空间关系**: --- #### **1. 方法区(Method Area)——JVM规范中的「仓库蓝图」** - **是什么**:JVM规范定义的**逻辑概念**,用于存储类信息、常量池、静态变量等元数据。 - **核心特点**: - **规范层面**:所有JVM实现必须要有方法区,但**不规定具体实现方式**(如同建筑蓝图只要求有仓库,不限制建材)。 - **存储内容**: - 类元数据(类名、方法字节码、字段描述等) - 运行时常量池 - 静态变量(JDK7后静态变量移到堆中) --- #### **2. 空间(Metaspace)——JDK8+的「现化仓库」** - **是什么**:**JDK8及之后**对方法区的**具体实现**,取了JDK7之前的**永久(PermGen)**。 - **关键变革**: | **对比项** | **永久(JDK7-)** | **空间(JDK8+)** | |----------------|----------------------------------|--------------------------------| | **物理位置** | 在JVM堆内存中划分域 | 使用**本地内存(Native Memory)** | | **内存管理** | 受JVM堆大小限制,需手动调优(-XX:PermSize) | **自动扩展**,默认无上限 | | **OOM风险** | 易出现`java.lang.OutOfMemoryError: PermGen space` | 更少OOM(除非物理内存耗尽) | | **垃圾回收** | Full GC时回收 | 特定条件下元数据可被卸载 | --- ### **3. 二者关系:规范 vs 实现** - **方法区是接口**:如同「仓库必须能存货物」的抽象要求。 - **空间是具体实现**:如同用「钢结构+智能管理系统」建造的真实仓库。 - **历史演变**: - JDK7-:方法区 = 永久(堆内实现) - JDK8+:方法区 = 空间(本地内存实现 + 压缩类指针优化) --- #### **4. 空间技术细节** - **两个子空间**: - **Metaspace**:存Klass结构(类的元数据) - **Compressed Class Space**:存压缩后的类指针(默认1G,通过`-XX:CompressedClassSpaceSize`调整) - **重要参数**: ```bash -XX:MetaspaceSize=128m # 初始大小 -XX:MaxMetaspaceSize=512m # 限制最大大小(防止本地内存耗尽) ``` --- ### **5. 为什么要用空间永久?** - **根本矛盾**:永久在堆内,元数据动态加载的特性不匹配。 - **三大痛点**: 1. **调优困难**:需预测永久大小,容易OOM 2. **GC低效**:Full GC时回收元数据,导致STW时间长 3. **兼容性差**:HotSpot其他JVM(如JRockit)的方法区实现难以统一 --- ### **6. 一句话总结** - **方法区JVM规范里的“仓库需求文档”,空间是JDK8+的“仓库施工方案”**。 - 从永久空间的升级,本质是让类元数据存储摆脱堆内存束缚,拥抱更灵活的本地内存管理!
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值