方法区的垃圾回收
1. 垃圾回收条件
方法区主要包含类的元信息、运行时常量池(就是存的类相关字节码信息)、字符串常量池(保存在堆上面,不涉及方法区的垃圾回收)。如果类不再使用使用,垃圾回收就可以在方法区回收相关的元信息和字节码信息。垃圾回收类的过程在类的生命周期里面叫做类的卸载。 判定一个类可以被卸载。需要同时满足下面三个条件:
1、此类所有实例对象都已经被回收,在堆中不存在任何该类的实例对象以及子类对象。
2、加载该类的类加载器已经被回收。
3、该类对应的java.lang.Class
对象没有在任何地方被引用。
public class Jvm09 {
public static void main(String[] args) throws Exception {
int count = 0;
while (true) {
MyClassloader classLoader = new MyClassloader();
classLoader.setBasePath("F:\\Code\\demo-test\\target\\classes\\");
Class<?> clazz = classLoader.loadClass("com.rocket.learn.jvm.UserInfo");
Object o = clazz.newInstance();
Thread.sleep(500);
clazz=null;
o = null;
classLoader = null;
// 告诉jvm进行垃圾回收
System.gc();
System.out.println(++count);
}
}
}
运行的时候加上JVM参数:-XX:+TraceClassLoading
和-XX:+TraceClassUnLoading
来看字节码装载和卸载的情况: while方法里面每次循环结束,下一次的时候while里面的变量都是失效,可以不用显示的置为null。可以看到当三个条件同时满足的时候就会将类进行卸载。
提示
调用System.gc()
方法并不一定会立即回收垃圾,仅仅是向Java虚拟机发送一个垃圾回收的请求,具体是否需要 执行垃圾回收Java虚拟机会自行判断。
2. 垃圾回收场景
实际开发的时候大部分我们的代码使用的类加载器是应用类加载器,它不会也不能置为null,这就是导致我们方法区的类基本上都不能被卸载回收,也就是方法区很少被回收。
开发中此类场景卸载类的场景也有,主要在如OSGi、JSP的热部署等应用场景中。每个jsp文件对应一个唯一的类加载器,当一个jsp文件修改了,就直接卸载这个jsp类加载器。重新创建类加载器,重新加载jsp文件。
3. 查看垃圾回收日志
如果想要查看垃圾回收的信息,可以使用-verbose:gc
参数。
语法:-verbose:gc
这里发生了两次GC, 其中GC减小了600多k(28344k-21408k)的大小,FullGC减小到200多k(21408k-21146k)。