JVM-024-运行时数据区-方法区(Method Area)-垃圾回收

概述

  • 有些人认为方法区(如Hotspot虚拟机中的元空间或者永久代)是没有垃圾收集行为的,其实不然。《Java虚拟机规范》对方法区的约束是非常宽松的,提到过可以不要求虚拟机在方法区中实现垃圾收集。事实上也确实有未实现或未能完整实现方法区类型卸载的收集器存在(如JDK11时期的ZGC收集器就不支持类卸载)。

  • 一般来说这个区域的回收效果比较难令人满意,尤其是类型的卸载,条件相当苛刻。但是这部分区域的回收有时又确实是必要的。以前sun公司的Bug列表中,曾出现过的若干个严重的Bug就是由于低版本的HotSpot虚拟机对此区域未完全回收而导致内存泄漏。

方法区的垃圾收集主要回收两部分内容:运行时常量池中废弃的常量和不再使用的类型(即:运行时常量池和类信息)。

运行时常量池回收(废弃常量)

方法区运行时常量池包括两类:字面量和符号引用

  1. 字面量:比较接近Java语言层次的常量概念,如文本字符串、被声明为final的常量值等。

    • 字面量是源代码中的固定值的表示法,即通过字面我们就能知道其值的含义。字面量包括整数、浮点数和字符串字面量。
  2. 符号引用:符号引用则属于编译原理方面的概念,常见的符号引用包括类符号引用、字段符号引用、方法符号引用、接口方法符号。包括下面三类常量:

    • 类和接口的全限定名

    • 字段的名称和描述符

    • 方法的名称和描述符

HotSpot虚拟机对运行时常量池的回收策略是很明确的,只要运行时常量池中的常量没有被任何地方引用,就可以被回收。回收废弃常量与回收Java堆中的对象非常类似。

类信息回收(类卸载)

  • 判定一个常量是否“废弃”还是相对简单,而要判定一个类型是否属于“不再被使用的类”的条件就比较苛刻了。需要同时满足下面三个条件:

    1. 该类所有的实例都已经被回收,也就是Java堆中不存在该类及其任何派生子类的实例。

    2. 加载该类的类加载器已经被回收,这个条件除非是经过精心设计的可替换类加载器的场景,如OSGi、JSP的重加载等,否则通常是很难达成的。

    3. 该类对应的java.lang.Class对象没有在任何地方被引用,无法在任何地方通过反射访问该类的方法。

  • Java虚拟机被允许对满足上述三个条件的无用类进行回收,这里说的仅仅是“被允许”,而并不是和对象一样,没有引用了就必然会回收。关于是否要对类型进行回收,HotSpot虚拟机提供了-Xnoclassgc参数进行控制,还可以使用-verbose:class 以及 -XX:+TraceClass-Loading、-XX:+TraceClassUnLoading查看类加载和卸载信息

  • 在大量使用反射、动态代理、CGLib等字节码框架,动态生成JSP以及OSGi这类频繁自定义类加载器的场景中,通常都需要Java虚拟机具备类型卸载的能力,以保证不会对方法区造成过大的内存压力。

JVM-024-运行时数据区-方法区(Method Area)-垃圾回收

https://blog.buubiu.com/JVM-024-运行时数据区-方法区-Method-Area-垃圾回收/

作者

buubiu

发布于

2022-06-22

更新于

2024-12-01

许可协议