5.1 定义

共享区域,主要存放类的组织结构(成员变量,方法、构造器的代码),常量池,类加载器。方法区在虚拟机启动时被创建。在逻辑上属于堆(具体产商实现时不一定划分在堆中)。在jdk1.8以前,方法区位于jvm的永久代,字符串存放在常量池。在jdk1.8以后,方法区则位于本地内存的元空间,字符串存在于堆。具体参考下图。
图片说明

5.2 方法区内存溢出

通过下面代码可以演示方法区内存溢出。

public class Demo1_8 extends ClassLoader { // 可以用来加载类的二进制字节码
    public static void main(String[] args) {
        int j = 0;
        try {
            Demo1_8 test = new Demo1_8();
            for (int i = 0; i < 10000; i++, j++) {
                // ClassWriter 作用是生成类的二进制字节码
                ClassWriter cw = new ClassWriter(0);
                // 版本号, public, 类名, 包名, 父类, 接口
                cw.visit(Opcodes.V1_8, Opcodes.ACC_PUBLIC, "Class" + i, null, "java/lang/Object", null);
                // 返回 byte[]
                byte[] code = cw.toByteArray();
                // 执行了类的加载
                test.defineClass("Class" + i, code, 0, code.length); // Class 对象
            }
        } finally {
            System.out.println(j);
        }
    }
}

在jdk1.8以前的版本,将永久代设置为8m: -XX:MaxPermSize=10m,在jdk1.8以后,将元空间设置为8m:-XX:MaxMetaspaceSize=8m,将会报出OutOfMemoryError.

在实际的工作场景中,spring、mybatis等框架都使用了cglib来动态生成class,因此框架使用不当是可能导致方法区内存溢出的。不过在jdk1.8以后方法区移到了元空间,空间充裕了很多,也由元空间进行垃圾回收,内存溢出的可能降低了。