首先谈谈什么是垃圾
- 垃圾就是在程序运行中没有任何指向的对象,这个对象就是需要被回收的垃圾。
既然提到垃圾,那么我们就需要知道如何找出垃圾,下面我们来谈谈如何找出垃圾的两种算法(垃圾标记的算法)
1.引用计数算法
何为引用计数算法?很简单即: 对每个对象保存一个整形的引用计数器属性,用于记录对象被引用的情况。如果该对象被引用一次,那么该对象的引用计数器就加一,反之则减一。若引用计数器的值为0则表示这个对象没有再被使用,则可对其进行回收。
(1)此方法比较简单,便于辨识,回收没有延迟性,而且判断的效率也比较可以。相对的缺点也比较明显,就是需要单独设字段存储计数器,从而增加存储开销,并且每次赋值都需要更新计数器,从而增加了时间开销。
(2) 但是这个方法在java里面是不用的,为什么呢?因为这个方法有个致命的缺陷那就是-----无法解决循环引用的问题
何为循环引用?画个图即可理解:
由图可知,引用s1与s2分别指向对象1与对象2,而对象1和对象2又分别相互指向,那么根据引用计数算法可知两个对象的计数器的值就为2,那么当我s1与s2同时断开了对两个对象的引用,那么此时两个对象的计数器的值都变成了1即:
由上图就可以发现问题了,已经没有引用指向两个对象了,但是他们的计数器的值自然为1,这就麻烦了,如果jav a使用这个算法得话,那得漏掉多少垃圾呀。所以这就是循环引用问题,引用计数法是没法解决的。
2.上面说了引用计数法,下面我们说说另外一种垃圾标记算法----可达性分析算法有叫(根搜索算法)
(1)顾名思义就是根据根(gc roots)来遍历搜索判断哪些对象是可达的,哪些对象是不可达的,不可达的就是垃圾。(该方法有效的解决了循环引用的问题)
画个图来简单理解一下:
由图可知,因为s0可以作为一个gc roots,并且从s0到对象st是可达的,所以对象st不是垃圾。
(2) java中Gc Roots 包含哪些元素呢?
A:虚拟机栈中引用的对象(局部变量表中的引用)
B:本地方法栈内(通常说的本地方法)引用的对象。
C:方法区中类静态属性引用的对象。
D:方法区中常量引用的对象。
E:所有被同步锁synchoronized持有的对象。
F: java虚拟机内部的引用。
G:反映java虚拟机内部情况的JMXBean, JVMTI中注册的回调,本地代码缓存等。
(3)需要注意的是:除了以上这些固定的gc roots以外,根据用户所选用的垃圾回收器以及当前回收的内存区域不同,还可以将其他对象,“临时性”的加入gc roots,从而构成完整的gc roots集合。
举个例子:当我们只需要收集年轻代中的垃圾时,那么这个时候可能存在一种情况就是有老年代中的某些对象引用年轻代中的对象,那么这个时候也需要将老年代中的某些对象作为gc roots。这里也涉及到一个回收垃圾时遇到跨代引用该怎么处理,不太请不的小伙伴可以阅读我前面的文章。
以上就是本人对两种垃圾搜索算法的理解,可能有些地方理解不太到位,或者有些地方理解有误,欢迎读者留言评论,大家一起交流并提升。