💎一站式轻松地调用各大LLM模型接口,支持GPT4、智谱、星火、月之暗面及文生图 广告
## 哪些区域需要垃圾收集 在[熟悉Java内存区域](001%E7%86%9F%E6%82%89Java%E5%86%85%E5%AD%98%E5%8C%BA%E5%9F%9F.md)文中,知道了JVM运行时数据区有堆、方法区、虚拟机栈、本地方法栈和程序计数器5个部分,后3个部分都是线程私有的,其中的数据会跟随线程死亡自动回收,所以不需要垃圾收集。而堆和方法区是线程共享的,故垃圾收集主要关注这2个部分。 学习了[垃圾判定算法与4大引用](004%E5%9E%83%E5%9C%BE%E5%88%A4%E5%AE%9A%E7%AE%97%E6%B3%95%E4%B8%8E4%E5%A4%A7%E5%BC%95%E7%94%A8.md)后,我们知道了如何判断对象是垃圾了。 接下来,我们就开始回收堆和方法区中的无效数据。 ## 回收堆 对于可达性分析中不可达的对象,也并不是没有存活的可能。 ### 判定 finalize() 是否有必要执行 JVM 会判断此对象是否有必要执行 `finalize() `方法,如果对象没有覆盖 `finalize() `方法,或者 `finalize()` 方法已经被虚拟机调用过,那么视为“没有必要执行”。那么对象基本上就真的被回收了。 如果对象被判定为有必要执行 `finalize()` 方法,那么对象会被放入一个 `F-Queue` 队列中,虚拟机会以较低的优先级执行这些 `finalize()`方法,但不会确保所有的 `finalize()` 方法都会执行结束。如果 `finalize()` 方法出现耗时操作,虚拟机就直接停止指向该方法,将对象清除。 ### 对象重生或死亡 如果在执行 `finalize()` 方法时,将 this 赋给了某一个引用,那么该对象就重生了。如果没有,那么就会被垃圾收集器清除。 **任何一个对象的 finalize() 方法只会被系统自动调用一次,如果对象面临下一次回收,它的 finalize() 方法不会被再次执行,想继续在 finalize() 中自救就失效了。** ## 回收方法区 方法区存放生命周期较长的已加载的类信息、常量、静态变量和即时编译器编译后的代码4个部分,主要回收: * 无用的类 * 废弃常量 ### 判定无用的类 判定一个类是无用的,条件比较苛刻: * 该类的所有对象都已被清除。 * 加载该类的`ClassLoader`被回收。 * 该类的`java.lang.Class`对象没有被任何地方引用,无法在任何地方通过反射访问该类的方法。 满足上面3个条件的类,虚拟机会进行回收。(通过`-Xnoclassgc`参数控制) 一个类被虚拟机加载进方法区,那么在堆中就会有一个代表该类的对象:`java.lang.Class`。这个对象在类被加载进方法区时创建,在方法区该类被卸载时清除。 在大量使用反射、动态代理、CGLib等字节码框架、动态生成JSP以及OSGI这类频繁自定义ClassLoader的场景都需要虚拟机具备类卸载的功能,以保证方法区不会溢出。 ### 判定废弃常量 条件:只要常量池中的常量不被任何变量或对象引用,那么这些常量就会被清除掉。 比如,一个字符串 "abc" 进入了常量池,但是当前系统没有任何一个 String 对象引用常量池中的 "abc" 常量,也没有其它地方引用这个字面量,必要的话,"abc"常量会被清理出常量池。 ## 参考资料 * 周志明 * 《深入理解Java虚拟机》