方法区
方法区:是各个线程共享的内存区域,用于存储已被虚拟机加载的类信息、常量、即时编译器编译后的代码等数据,虽然 Java 虚拟机规范把方法区描述为堆的一个逻辑部分,但是也叫 Non-Heap(非堆)
方法区是一个 JVM 规范,永久代与元空间都是其一种实现方式
方法区的大小不必是固定的,可以动态扩展,加载的类太多,可能导致永久代内存溢出 (OutOfMemoryError)
方法区的 GC:针对常量池的回收及对类型的卸载,比较难实现
为了避免方法区出现 OOM,在 JDK8 中将堆内的方法区(永久代)移动到了本地内存上,重新开辟了一块空间,叫做元空间,元空间存储类的元信息,静态变量和字符串常量池等放入堆中
类元信息:在类编译期间放入方法区,存放了类的基本信息,包括类的方法、参数、接口以及常量池表
常量池表(Constant Pool Table)是 Class 文件的一部分,存储了类在编译期间生成的字面量、符号引用,JVM 为每个已加载的类维护一个常量池
- 字面量:基本数据类型、字符串类型常量、声明为 final 的常量值等
- 符号引用:类、字段、方法、接口等的符号引用
运行时常量池是方法区的一部分
- 常量池(编译器生成的字面量和符号引用)中的数据会在类加载的加载阶段放入运行时常量池
- 类在解析阶段将这些符号引用替换成直接引用
- 除了在编译期生成的常量,还允许动态生成,例如 String 类的 intern()
本地内存
基本介绍
虚拟机内存:Java 虚拟机在执行的时候会把管理的内存分配成不同的区域,受虚拟机内存大小的参数控制,当大小超过参数设置的大小时就会报 OOM
本地内存:又叫做堆外内存,线程共享的区域,本地内存这块区域是不会受到 JVM 的控制的,不会发生 GC;因此对于整个 Java 的执行效率是提升非常大,但是如果内存的占用超出物理内存的大小,同样也会报 OOM
本地内存概述图:
元空间
JDK 8中PermGen 被元空间代替,永久代的类信息、方法、常量池等都移动到元空间区
元空间与永久代区别:元空间不在虚拟机中,使用的本地内存,默认情况下,元空间的大小仅受本地内存限制
方法区内存溢出:
JDK1.8 以前会导致永久代内存溢出:java.lang.OutOfMemoryError: PerGen space
1
-XX:MaxPermSize=8m #参数设置
JDK1.8 以后会导致元空间内存溢出:java.lang.OutOfMemoryError: Metaspace
1
-XX:MaxMetaspaceSize=8m #参数设置
元空间内存溢出演示:
1 | public class Demo1_8 extends ClassLoader { // 可以用来加载类的二进制字节码 |