- 一般来说,所有指向对象的引用都已失效,不可能再有程序能调用到这个对象,那么这个对象就成了垃圾,应该被回收。
- 根据这个思路,很容易就能想到用《引用计数》的办法来确定一个对象是否是垃圾。即每当多一个引用指向对象时,引用计数加一,每当少一个引用指向对象时,引用计数减一,引用计数减到零,对象就可以被回收了。
- 然而引用计数有一个致命问题不好解决,就是循环引用的问题。比如说一个循环链表,他们循环引用者,引用计数永远不会为零,但是实际上程序已经不能访问他们了,他们应该被回收。(如果还没明白循环引用,第五步之后有代码说明)
- 所以Java实际上是使用基于GC Roots的可达性分析,什么是GC Roots?所有类的静态变量,每个线程调用栈上的本地变量(实际上我们编程时也是要从这些地方开始访问数据),所有这些对象,以及被这些对象所指向的对象,都是活的对象。活的对象所指向的对象也是活的对象。GC通过有向图的进行可达性分析,不可达的对象就被视为是垃圾对象。
- 所以只要在GC的时刻,让程序暂停运行,然后从GC Roots开始分析,最后没有被标记为活对象的对象就是垃圾了。
*代码事例解释什么是循环引用:
如上图所示,假设我们有两个类分别是A和B,A类中有一个字段是B类的类型,B类中有一个字段是A类类型,现在分别new一个A类对象和new一个B类对象,此时引用a指向刚new出来的A类对象,引用b指向刚new出来的B类对象,然后将两个类中的字段互相引用一下,这样即使下面进行a = null和b = null,但是A类对象仍然被B类对象中的字段引用着,尽管现在A类和B类独享都已经访问不到了,但是引用计数却都不为0.
三.Java中有哪几种引用
- 强引用(StrongReference):强引用就是我们平时使用的引用,是使用最普遍的引用。如果一个对象具有强引用,那垃圾回收器绝不会回收它(结合上面,一个对象有强引用,那么这个对象一定可达,也就是说明这个对象是活的)。当内存空间不足,Java虚拟机宁愿抛出OutOfMemoryError错误,使程序异常终止,也不会靠随意回收具有强引用的对象来解决内存不足的问题。(简记:永不回收)
- 软引用(SoftReference):如果一个对象只具有软引用,则内存空间足够,垃圾回收器就不会回收它;如果内存空间不足了,就会回收这些对象的内存。只要垃圾回收器没有回收它,该对象就可以被程序使用。所以,软引用可用来实现内存敏感的高速缓存。(简记:内存不足再回收)
- 弱引用(WeakReference):弱引用与软引用的区别在于:弱引用的对象拥有更短暂的生命周期。在垃圾回收器线程扫描它所管辖的内存区域的过程中,一旦发现了只具有弱引用的对象,不管当前内存空间足够与否,都会回收它的内存。不过,由于垃圾回收器是一个优先级很低的线程,因此不一定会很快发现那些只具有弱引用的对象。(简记:遇到就回收)
- 虚引用(PhantomReference):“虚引用”顾名思义,就是形同虚设,与其他几种引用都不同,虚引用并不会决定对象的生命周期。如果一个对象仅持有虚引用,那么它就和没有任何引用一样,在任何时候都可能被垃圾回收器回收。虚引用主要用来跟踪对象被垃圾回收器回收的活动。虚引用与软引用和弱引用的一个区别在于:虚引用必须和引用队列(ReferenceQueue)联合使用。当垃圾回收器准备回收一个对象时,如果发现它还有虚引用,就会在回收对象的内存之前,把这个虚引用加入到与之关联的引用队列中。(简记:形同虚设)