1.java 堆中几乎放着所有对象的实例, 那么什么样子的对象才是可以被回收的呢?
1.1. 引用计数法:
给对象添加一个引用计数器, 当有地方引用的时候, 计数器就 + 1, 引用失效就 - 1; 任何时候当计数器为 0, 那么这个对象就是可以被回收的. 该方法实现简单, 效率也高, 但是并没有被主流的虚拟机采用, 因为很难解决对象互相循环引用问题.
1.2. 可达性分析算法
这个的基本思想就是通过一系列的 "GC Roots" 作为对象的起点, 从这些节点开始向下搜索, 节点所走过的路径称为引用链, 当一个对象到 GC Roots 没有任何引用链相连的话, 则证明此对象是不可用的.
那些可以作为 GC Roots 节点呢? 一般来说类加载器, Thread, 虚拟机栈的本地变量表, static 成员, 常量引用, 本地方法栈的变量等等.
1.3 finalize() 方法 (只会被调用一次)
即使在可达性分析算法中不可达的对象, 也并非是 "非死不可" 的, 这时候它们暂时处于 "缓刑" 阶段, 要真正宣告一个对象死亡, 至少要经历再次标记过程.
标记的前提是对象在进行可达性分析后发现没有与 GC Roots 相连接的引用链.
1.3.1. 第一次标记并进行一次筛选.
筛选的条件是此对象是否有必要执行 finalize() 方法.
当对象没有覆盖 finalize 方法, 或者 finzlize 方法已经被虚拟机调用过, 虚拟机将这两种情况都视为 "没有必要执行", 对象被回收.
1.3.2. 第二次标记
如果这个对象被判定为有必要执行 finalize() 方法, 那么这个对象将会被放置在一个名为: F-Queue 的队列之中, 并在稍后由一条虚拟机自动建立的, 低优先级的 Finalizer 线程去执行. 这里所谓的 "执行" 是指虚拟机会触发这个方法, 但并不承诺会等待它运行结束. 这样做的原因是, 如果一个对象 finalize() 方法中执行缓慢, 或者发生死循环 (更极端的情况), 将很可能会导致 F-Queue 队列中的其他对象永久处于等待状态, 甚至导致整个内存回收系统崩溃.
finalize() 方法是对象脱逃死亡命运的最后一次机会, 稍后 GC 将对 F-Queue 中的对象进行第二次小规模标记, 如果对象要在 finalize() 中成功拯救自己 ---- 只要重新与引用链上的任何的一个对象建立关联即可, 譬如把自己赋值给某个类变量或对象的成员变量, 那在第二次标记时它将移除出 "即将回收" 的集合. 如果对象这时候还没逃脱, 那基本上它就真的被回收了.
1.4 如何判断一个常量是废弃常量
假如在常量池中存在字符串 "abc", 如果当前没有任何 String 对象引用该字符串常量的话, 就说明常量 "abc" 就是废弃常量, 如果这时发生内存回收的话而且有必要的话,"abc" 就会被系统清理出常量池.
1.5 如何判断一个类是无用的类
1. 该类所有的实例都已经被回收, 也就是 Java 堆中不存在该类的任何实例.
2. 加载该类的 ClassLoader 已经被回收.
3. 该类对应的 java.lang.Class 对象没有在任何地方被引用, 无法在任何地方通过反射访问该类的方法.
但是并不是和对象一样不使用了就会必然被回收.
来源: https://www.cnblogs.com/nijunyang/p/11108441.html