JVM--方法区(Method Area)

本文探讨了JVM中方法区的概念,包括其在不同JVM实现中的具体表现,如永久代和元空间。详细解释了从JDK7到JDK8中方法区的变化,以及字符串、类元数据和静态变量的存储位置变化。
部署运行你感兴趣的模型镜像

方法区

供各线程共享的运行时内存区域。它存储了每一个类的结构信息,例如运行时常量池(Runtime Constant Pool)、字段和方法数据、构造函数和普通方法的字节码内容。

上面说的是规范,在不同虚拟机里实现是不一样的,最典型的就是永久代(PermGen)和元空间(Metaspace)

实例变量存在堆内存中,和方法区无关。

在实际的 JVM 实现中,它不一定是由单一的特殊区域所实现,不同的实现可以放在不同的地方:

永久代(Permanent Generation)

永久代是 HotSpot 虚拟机在 JDK7 及以前对方法区的具体实现,而永久代也在 heap 中(但注意,其它虚拟机并没有永久代,这是 HotSpot 虚拟机特有的)。

在 JDK1.7 中,移除永久代的工作就已经开始了,原先存储在永久代的部分数据转移到了Java heap 或者 native memory,但永久代仍存在于 JDK1.7 中,并没完全移除。移除工作主要包括下面三点:

  • 符号引用(Symbolic Reference)转移到了native memory
  • Interned Strings转移到了heap
  • 静态变量从instanceKlass对象(PermGen内)末尾转移到了java.lang.Class对象(heap内)的末尾

元空间(metaspace)

从 JDK 8 开始的 HotSpot 虚拟机完全移除了 PermGen,改为在 native memory 里存放这些元数据。新的用于存放元数据的内存空间叫做 Metaspace。

取消永久代主要有以下几点原因:

  • 字符串存在永久代中,容易出现性能问题和内存溢出
  • 永久代会为 GC 带来不必要的复杂度,并且回收效率偏低
  • 便于将 HotSpot 与 JRockit 合二为一(JRockit 中并没有永久代)

在 Java 8 中抛出堆溢出的示例:

public class StringHeapError {
    public static void main(String[] args) {
        String temp = "world";
        for (int i = 0; i < Integer.MAX_VALUE; i++) {
            String str = temp + temp;
            temp = str;
            str.intern();
        }
    }
}

输出:

Exception in thread "main" java.lang.OutOfMemoryError: Java heap space

如果是在 JDK 6 中,这会提示 PermGen Space 溢出

下面是抛出 Metaspace 溢出的例子,用了 Cglib 生成大量的代理类。不过在这之前需要修改JVM启动参数,设置为:

-XX:MetaspaceSize=10M -XX:MaxMetaspaceSize=10M
public class ClassInfo {
    public static void main(String[] args) {
        while (true) {
            Enhancer enhancer = new Enhancer();
            enhancer.setSuperclass(OOMObject.class);
            enhancer.setUseCache(false);
            enhancer.setCallback(new MethodInterceptor() {
                public Object intercept(Object obj, Method method, Object[] args, MethodProxy proxy) throws Throwable {
                    return proxy.invokeSuper(obj, args);
                }
            });
            enhancer.create();
        }
    }

    static class OOMObject { }
}

输出:

Exception in thread "main" java.lang.OutOfMemoryError: Metaspace

可以看到,类信息(classes metadata)放到了 Metaspace 中,而 Interned Strings、静态变量放到了 heap 上。

您可能感兴趣的与本文相关的镜像

ACE-Step

ACE-Step

音乐合成
ACE-Step

ACE-Step是由中国团队阶跃星辰(StepFun)与ACE Studio联手打造的开源音乐生成模型。 它拥有3.5B参数量,支持快速高质量生成、强可控性和易于拓展的特点。 最厉害的是,它可以生成多种语言的歌曲,包括但不限于中文、英文、日文等19种语言

### JVM 方法区与栈的区别和作用 #### 方法区的作用与特点 方法区Method Area)是 JVM 中一块用于存储已被虚拟机加载的类信息、常量、静态变量以及编译后的代码等数据的内存区域。它是所有线程共享的内存区域,主要用于支持 Java 程序运行时的类加载机制[^1]。 - **存储内容**:方法区中存储的内容包括类的元信息(如字段、方法数据)、运行时常量池、静态变量等。此外,它还可能包含即时编译器优化后生成的本地代码[^2]。 - **非堆特性**:尽管 JVM 规范将方法区描述为堆的一个逻辑部分,但它通常被称为“非堆”(non-heap),以区别于堆内存[^4]。 - **垃圾回收**:方法区中的内存回收主要针对不再使用的类信息进行清理,但其回收频率远低于堆内存。 #### 栈的作用与特点 栈(Stack)是 JVM 中每个线程私有的内存区域,用于存储方法执行过程中的局部变量表、操作数栈、动态链接、方法出口等信息。栈的生命周期与线程相同,线程启动时创建,线程结束时销毁[^5]。 - **存储内容**:栈中存储的是方法执行过程中所需的局部变量(包括基本数据类型和对象引用)、中间计算结果等。每个方法在执行时都会创建一个栈帧(Stack Frame),栈帧中包含该方法的所有局部变量和操作数栈。 - **线程私有性**:每个线程都有自己独立的栈,线程之间无法直接访问彼此的栈内容。 - **异常处理**:如果线程请求的栈深度超过允许的最大值,则会抛出 `StackOverflowError`;如果 JVM 无法动态扩展栈空间,则会抛出 `OutOfMemoryError`[^3]。 #### 方法区与栈的区别 | 特性 | 方法区 | 栈 | |------------------|--------------------------------------------|---------------------------------------------| | **内存分配** | 所有线程共享 | 每个线程独占 | | **存储内容** | 类信息、常量、静态变量、编译后的代码等 | 局部变量、操作数栈、方法出口等 | | **生命周期** | JVM 启动时创建,关闭时销毁 | 线程启动时创建,线程结束时销毁 | | **异常** | 可能抛出 `OutOfMemoryError` | 可能抛出 `StackOverflowError` 或 `OutOfMemoryError` | | **用途** | 支持类加载机制 | 支持方法执行过程中的数据存储和计算 | #### 方法区与栈的关系 方法区和栈虽然功能不同,但在 JVM 的整体内存管理中相辅相成: - **类信息的加载**:方法区存储了类的元信息和静态变量,当某个线程调用类的方法时,栈帧会从方法区中获取必要的类信息和静态变量[^1]。 - **方法执行的支持**:栈负责存储方法执行过程中的局部变量和操作数栈,而方法区则提供了方法执行所需的类定义和编译后的代码[^2]。 - **线程隔离与共享**:方法区是所有线程共享的,确保类信息的一致性和高效复用;而栈是线程私有的,保证了方法执行的安全性和独立性[^3]。 ```java // 示例代码:展示方法区和栈的协作 public class Example { public static int staticVar = 10; // 存储在方法区 public static void main(String[] args) { int localVar = 20; // 存储在栈中 System.out.println(staticVar + localVar); // 方法区和栈的协作 } } ```
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值