JVM 中的程序计数器、java 虚拟机栈、本地方法栈均属于线程私有,其生命周期控制在线程生命周期范围内,并且 java 虚拟机栈和本地方法栈中的的栈帧会随着对应方法的执行而出栈和入栈,由生到灭,所以这些区域的内存使用是确定性的(编译期已知)。然而,Java 堆和方法区是线程共享的,对象的创建也是动态的,所以垃圾收集的主战场集中在 java 堆和方法区。
相对于 java 堆而言,方法区的垃圾收集性价比要低很多,JVM 规范甚至不强制要求 JVM 在方法区实现垃圾收集。不过对于复杂应用,尤其是大量使用反射、动态代理、CGLib 等字节码框架,动态生成 JSP,以及 OSGi 这类频繁自定义类加载器的场景中,对于方法区实现垃圾收集还是有必要的。方法区的垃圾收集主要回收两部分内容:废弃的常量和不再使用的类型。对于一个类型是否不再被使用,JVM 在判定时需要同时满足以下 3 个条件:
- 该类所有的实例都已经被回收了,也就是说堆中不存在该类及其派生子类的任何实例。
- 加载该类的类加载器已经被回收了。
- 该类对应的 Class 类对象没有任何地方被引用,无法在任何地方通过反射访问该类的方法。