方法区
# 一. 概述
- 方法区(Method Area)与堆一样,也是所有线程共享的内存区域。
- 《Java虚拟机规范》明确: 尽管多有的方法区在逻辑上属于堆的一部分,但简单的实现可能不会选择去进行垃圾收集或者进行压缩。 所以方法区看做是一块独立于Java堆的内存空间
- 方法区的大小,跟堆一样可以选择固定大小或者可扩展
- 方法区在JVM启动的时候被创建,并且它的实际的物理内存空间中和Java堆区一样都可以是不连续的。
- 关闭JVM就会释放这个区域的内存。
# 二. 方法区大小的设置
jdk1.8:
-XX:MetaspaceSize 设置初始的元空间大小
-XX:MaxMetaspaceSize 设置最大的元空间大小
# 三. 方法区内存结构
方法区用于存储已被虚拟机加载的类型信息、常量、静态变量、即时编译器编译后的代码缓存等。以下做详细介绍:
# 3.1 类型信息
对每个加载的类型(类class、接口interface、枚举enum、注解annotation)。JVM必须在方法区中存储以下类型信息。
- 这个类型的完整有效名称。
- 这个类型直接父类的完整有效名。
- 这个类型的修饰符(public、abstract、final的某个子集)。
- 这个类型直接接口的一个有序列表。
# 3.2 方法信息
JVM 必须保存所有方法:
- 方法名称。
- 方法的返回类型。
- 方法参数的数量和类型。
- 方法的修饰符(public,private,protected,static,final,synchronized,native,abstract 的一个子集)
- 方法的字符码(bytecodes)、操作数栈、局部变量表及大小(abstract 和 native 方法除外)异常表(abstract 和 native 方法除外)
- 每个异常处理的开始位置、结束位置、代码处理在程序计数器中的偏移地址、被捕获的异常类的常量池索引。
# 3.3 域信息
JVM 必须在方法区中保存类型的所有域的相关信息以及域的声明顺序
- 域的相关信息包括:域名称、域类型、域修饰符(public、private、protected、static、final、volatile、transient 的某个子集)
# 3.4 运行时常量池
字节码文件内部包含了常量池。 方法区内部包含了运行时常量池。当字节码加载过后到方法区 常量池也就变成了运行时常量池。
# 3.4.1 class文件中的常量池(Constant Poll)
一个有效的字节码文件除了包含类的版本信息、字段、方法以及接口等描述信息,还包含一项信息就是常量池(Constant Pool Table)。包含各种字面量和对类型、域和方法的符号引用。 如下图所示:
常量池内存储的数据类型包括:
- 数量值
- 字符串值
- 类引用
- 字段引用
- 方法引用
# 3.4.2 运行时常量池小节
- 常量池表(Constant Poll Table)是Class文件的一部分,用于存放编译期生成的各种字面量与符号引用,这部分内容将在类加载后存放到方法区运行时常量池中。
- 运行时常量池中包含多种不同的常量,包含编译器就已经明确的数值字面量,也包括到运行期解析后才能够获得的方法或者字段引用。此时不再是常量池中的符号地址,这里换位真是地址。
# 四. 方法区的垃圾收集
方法区的垃圾收集主要回收两部分内容: 常量池中废弃的常量和不在使用的类型
方法区内常量池之中主要存放的两大类常量:字面量和符号引用。字面量比较接近 Java 语言层次的常量概念,如文本字符串、被声明为 final 的常量值等。而符号引用则属于编译原理方面的概念,包括下面三类常量:
- 类和接口的全限定名
- 字段的名称和描述符
- 方法的名称和描述符
HotSpot 虚拟机对常量池的回收策略是很明确的,只要常量池中的常量没有被任何地方引用,就可以被回收
判定一个类型是否属于“不再被使用的类”,需要同时满足三个条件:
- 该类所有的实例都已经被回收,也就是 Java 堆中不存在该类及其任何派生子类的实例。
- 加载该类的类加载器已经被回收,这个条件除非是经过精心设计的可替换类加载器的场景,如 OSGi、JSP 的重加载等,否则通常很难达成。
- 该类对应的 java.lang.Class 对象没有在任何地方被引用,无法在任何地方通过反射访问该类的方法。
方法区是否被GC,《Java虚拟机规范》没有明确规定。所以可以回收也可以不回收。而需要回收时回收的是常量池中废弃的常量和不在使用的类型
编辑 (opens new window)
上次更新: 2023/02/04, 14:30:19