引用计数(Reference Counting)算法
在对象中添加一个引用计数器,每当有一个地方引用它时,计数器值加一;当引用失效时,计数器值减一;任何时刻,计数器值为零的对象可被回收。
优点:只占用一点额外空间用于计数,原理简单,效率高
缺点:有很多边界条件需要考虑,如相互循环引用问题很难解决
可达性分析(Reachability Analysis)算法
通过一系列称为“GC Roots”的根对象作为起始节点集,从这些节点开始,根据引用关系向下搜索,搜索过程所走过的路径称为“引用链”(Reference Chain),如果某个对象到 GC Roots 间没有任何引用链相连(即不可达),则此对象可被回收。
可以作为 GC Roots 的对象,包括以下几种:
- 在虚拟机栈(栈桢中的本地变量表)中引用的对象,譬如各个线程被调用的方法堆栈中使用到的参数、局部变量、临时变量等。
- 在方法区中类静态属性引用的对象,譬如 Java 类的引用类型静态变量
- 在方法区中常量引用的对象,譬如字符串常量池(String Table)里的引用
- 在本地方法栈中 JNI(即 Native 方法)引用的对象。
- Java 虚拟机内部的引用,如基本数据类型对应的 Class 对象,一些常驻的异常对象(如 NullPointException、OutOfMemoryError)等,还有系统类加载器。
- 所有被同步锁(synchronized 关键字)持有的对象
- 反映 Java 虚拟机内部情况的 JMXBean、JVMTI 中注册的回调、本地代码缓存等。 另外,不同的拉架收集器以及回收的内存区域不同,还可以有其他对象“临时”加入,共同构成完整的 GC Roots 集合。
四种引用
- 强引用(Strongly Reference):传统意义上的引用,只要强引用存在,则对象永远不能被垃圾收集器回收掉。
- 软引用(Soft Reference):用于描述一些还有用,但非必须的对象。在系统将发生内存溢出时进行回收,如果回收完仍没有足够内存,则抛出内存溢出异常。
- 弱引用(Weak Reference):非必须得对象,被弱引用关联的对象只能生存到下一次垃圾收集。无论内存是否足够,都会回收这部分对象。
- 虚引用(Phantom Reference):无法通过虚引用来获取一个对象实例,存在的目的是为了在对象被回收时收到系统通知。
死亡标记
对象死亡至少要经过两次标记,这两次标记的作用如下:
- 第一次标记,对象在进行可达性分析后没有与 GC Roots 相连接的引用链。随后根据对象是否需要执行 finalize() 方法,进行一次筛选。
- 如果对象没有覆盖 finalize() 方法或者 finalize() 方法已经被虚拟机调用,则这两种情况都被认定为没必要执行。
- 如果对象被判定为需要执行 finalize() 方法,则该对象将会被放在一个名为 F-Queue 的队列中,稍后会有一条由虚拟机自动建立的、低调度优先级的 Finalizer 线程去执行它们的 fianlize() 方法(线程不保证一定会等待该方法运行结束)。
- 第二次标记,收集器会对 F-Queue 中的对象进行第二次标记,被第二次标记后的对象将被真的回收。
- 如果对象要在 finalize() 方法中拯救自己,只需要与引用链上的任何一个对象建立关联,那么在第二次标记时会将它移出“即将回收”的集合。
- 这种自救机会只有一次,因为一个对象的 finalize() 方法最多只会被系统自动调用一次
- finalize() 方法在 jdk9 中被标记为已弃用