Skip to content

方法区的垃圾回收

1. 垃圾回收条件

方法区主要包含类的元信息、运行时常量池(就是存的类相关字节码信息)、字符串常量池(保存在堆上面,不涉及方法区的垃圾回收)。如果类不再使用使用,垃圾回收就可以在方法区回收相关的元信息和字节码信息。垃圾回收类的过程在类的生命周期里面叫做类的卸载。
Alt text 判定一个类可以被卸载。需要同时满足下面三个条件:
1、此类所有实例对象都已经被回收,在堆中不存在任何该类的实例对象以及子类对象。
2、加载该类的类加载器已经被回收。
3、该类对应的java.lang.Class对象没有在任何地方被引用。

java
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来看字节码装载和卸载的情况:
Alt text while方法里面每次循环结束,下一次的时候while里面的变量都是失效,可以不用显示的置为null。可以看到当三个条件同时满足的时候就会将类进行卸载。

提示

调用System.gc()方法并不一定会立即回收垃圾,仅仅是向Java虚拟机发送一个垃圾回收的请求,具体是否需要 执行垃圾回收Java虚拟机会自行判断。

2. 垃圾回收场景

实际开发的时候大部分我们的代码使用的类加载器是应用类加载器,它不会也不能置为null,这就是导致我们方法区的类基本上都不能被卸载回收,也就是方法区很少被回收。
开发中此类场景卸载类的场景也有,主要在如OSGi、JSP的热部署等应用场景中。每个jsp文件对应一个唯一的类加载器,当一个jsp文件修改了,就直接卸载这个jsp类加载器。重新创建类加载器,重新加载jsp文件。

3. 查看垃圾回收日志

如果想要查看垃圾回收的信息,可以使用-verbose:gc参数。
语法:-verbose:gc
Alt text 这里发生了两次GC, 其中GC减小了600多k(28344k-21408k)的大小,FullGC减小到200多k(21408k-21146k)。