垃圾回收算法
1.引用计数法
1.1思想
对于对象设置一个引用计数器,每增加一个变量对它的引用,引用计数器就会 +1,每减少一个变量的引用,引用计数器就会 -1,只有当对象的引用计数器 =0时,该对象才会被回收。
1.2优缺点
1.每次在增加变量引用和减少引用时都要进行
加法或
减法操作,如果频繁操作对象的话,在一定程度上影响系统的性能。
2.无法处理
循环引用的情况。
循环引用:假设有两个对象 A和B,A中引用了B对象,并且B中也引用了A对象,那么这时两个对象的引用计数器都不为0,但是由于存在相互引用导致无法垃圾回收A和B,导致
内存泄漏。
注意:由于引用计数器算法存在循环引用以及性能的问题,java虚拟机并未使用此算法作为垃圾回收算法。
2.标记清除法
2.1思想
可达对象:通过根对象的进行引用搜索,最终可以到达的对象。
不可达对象:通过根对象进行引用搜索,最终没有被引用到的对象。
2.2标记阶段
通过根节点,标记所有从根节点开始的可达对象,因此未被标记的对象就是未被引用的垃圾对象。
2.3清除阶段
清除所有未被标记的对象
注意:这种方法可以解决循环引用的问题,只有两个对象不可达,即使它们互相引用也无济于事。也是会被判定位不可达对象。
2.4优缺点
缺点:垃圾回收后可能存在大量的磁盘碎片,准确的说是内存碎片。因为对象所占用的地址空间是固定的。
2.5总结
标记清除算法可能产生的最大的问题就是空间碎片。
3.复制算法
3.1思想
将原有的内存空间分为两块相同的存储空间,每次只使用一块,在垃圾回收时,
将正在使用的内存块中存活对象复制到未使用的那一块内存空间中,之后清除正在使用的内存块中的所有对象,完成垃圾回收。
3.2适用场景
系统中的垃圾对象很多,复制算法需要复制的存活对象就会相对较少。因此,在真正需要垃圾回收的时刻,复制算法的效率是很高的。
3.2优点
由于存活对象在垃圾回收过程中是一起被赋值到另一块内存空间中的,因此,可确保回收的内存空间是没有碎片的。
3.3缺点
复制算法的代价是将系统
内存空间折半,只使用一半空间,而且如果内存空间中垃圾对象少的话,
复制对象也是很耗时的,因此,单纯的复制算法也是不可取的。
注意:复制算法比较适用于新生代。因为在新生代中,垃圾对象通常会多于存活对象,算法的效果会比较好
4.标记压缩算法
4.1思想
和标记清除算法一样,标记压缩算法也首先从根节点开始,对所有可达的对象做一次标记,但之后它并不是简单的清理未标记的对象,而是将所有的存活对象压缩到内存空间的一端,之后清理边界外所有的空间。这样做避免的碎片的产生,又不需要两块相同的内存空间,因此
性价比高。
注意:标记压缩算法比较适用于老年代。因为在老年代中,大部分对象都是存活的对象,垃圾对象通常会少于存活对象,标记压缩算法的效果会比较好
5.分代算法
5.1思想
将内存空间根据对象的特点不同进行划分,选择合适的垃圾回收算法,以提高垃圾回收的效率。
新生代:存放年轻对象的堆空间,年轻对象指刚刚创建,或者经历垃圾回收次数不多的对象。---复制算法
老年代:存放老年对象的堆空间。即为经历多次垃圾回收依然存活的对象。---标记清除算法/标记压缩算法
5.2算法详述
通常新生代回收的频率很高,但是每次回收的时间都很短,而老年代回收的频率比较低,但是被消耗很多的时间。为了支持高频率的新生代回收,虚拟机可能使用一种叫做卡表的数据结构,卡表为一个比特位集合,每一个比特位可以用来表示老年代的某一区域中的所有对象是否持有新生代对象的引用,新生代GC时,可以不用花大量时间扫描所有老年代对象,来确定每一个对象的引用关系,而可以先扫描卡表,只有当卡表的标记为1时,才需要扫描给定区域的老年代对象,而卡表为0的所在区域的老年代对象,一定不含有新生代对象的引用。
6.分区算法
6.1思想
将整个堆空间划分为连续的不同小区间,每一个小区间都独立使用,独立回收。
6.2优点
可以控制一次回收多少个小区间。通常,相同的条件下,堆空间越大,一次GC所需的时间就越长,从而产生的停顿时间就越长。为了更好的控制GC产生的停顿时间,将一块大的内存区域分割成多个小块,根据目标的停顿时间,每次
合理的回收若干个小区间,而不是整个堆空间,从而减少一个GC的停顿时间。