(1)引用计数法
思想:每一个对象都有一个counter,只要有任何一个对象引用了该对象,则其counter加1,当引用失效时,counter减1,当counter为0时,对象不存在任何引用,在GC时被清除
GC过程:counter在每次引用生效和失效时进行加减法操作,并判断是否为0,是则清除
优点:思想和实现都很简单(只需要为每一个对象配备一个整型的计数器)
缺点:1、无法处理循环引用问题,可能会造成死锁:比如对象A和对象B互相引用,但是不存在其他对象引用A和B,此时A和B属于不可达的对象,即垃圾对象,但是counter无法识别此类垃圾对象间的互相引用,从而引起内存泄露,不能GC
2、counter要求在每次引用生效和失效时进行加减法操作,在一定程度上影响系统性能
相关概念:可达对象和不可达对象:通过根对象的进行引用搜索,最终可以到达的对象为可达对象,可达对象即为存在引用的对象,反之,则为不可达对象
内存泄漏:内存空间使用完毕之后未回收
图解:
注意:Java虚拟机不使用引用计数法GC
(2)标记清除法
思想:标记所有的可达对象(存在引用的对象),则未被标记的对象就是不存在引用的垃圾对象,GC时清除所有未被标记的对象
GC过程 标记清除法的GC时经历标记 + 清除两个过程,先标记,后清除
优点:只存在循环引用不存在其他引用的对象不会被标记,解决了循环引用问题
缺点:可能会产生空间碎片(不连续的内存空间),不连续的内存空间在内存分配时的工作效率低于连续的内存空间,尤其是对大对象的的内存分配
相关概念:空间碎片:不连续的内存空间
图解:
(3)复制算法
思想:将内存空间分为两块相同的存储空间,每次只使用一块,GC时,将正在使用的内存中的存活对象复制到另一块存储空间中,然后清除正在使用的空间的所有对象
GC过程:先复制,再清除
优点:存活对象相对少时,效率很高(因为需要复制的对象少),存活对象复制到另一空间时,解决了空间碎片问题
缺点:系统内存只能使用一半的内存空间,而且如果存活对象相对多的话,比较耗时
图解:
应用场景:java新生代串行垃圾回收器
新生代分为eden空间、from空间和to空间3个部分,其中from和to空间是两块相同的空间,同一时间只使用其中一块空间,另一空间用于GC时存放复制的存活对象,from和to空间也称为survivor空间,用于存放未被回收的对象。
GC过程:在垃圾回收时,eden空间中存活的对象会被复制到未使用的survivor空间中(图中的to),正在使用的survivor空间(图中的from)中的年轻对象也会被复制到to空间中(大对象或者老年对象会直接进入老年代,如果to空间已满,则对象也会进入老年代)。此时eden和from空间中剩余对象就是垃圾对象,直接清空,to空间则存放此次回收后存活下来的对象。
优点:保证了内存空间的连续性,又避免了大量的空间浪费
相关概念:
新生代对象:刚刚创建或者经历垃圾回收次数不多的对象。
老年代对象:经历多次垃圾回收依然存活的对象。
图解:
注意:复制算法比较适用于新生代。因为在新生代中,垃圾对象通常会多于存活对象
(4)标记压缩法(标记清除压缩法)
思想:标记压缩法是对标记清除法的优化,所以也叫标记清除压缩法。和标记清除法一样,先标记所有的可达对象(存在引用的对象),不同的是,标记完成后并不是直接清除未标记的垃圾对象,而是将所有的被标记的对象(即存活对象)压缩到内存空间的一端后在清理边界外所有的空间。
GC过程:分为标记+压缩 + 清除三个步骤
优点:解决了标记清除法带来的空间碎片问题,又不需要折损可使用空间(复制算法折损了可使用空间)
图解:
应用场景:老年代的回收算法
新生代存活对象少,垃圾对象多,所以使用复制算法效率高(要复制的对象少),老年代刚好相反,存活对象多,垃圾对象少,复制算法不适用,使用标记压缩法(个人感觉是一个排除法,复制算法不适用,标记清除法产生空间碎片,也不适用,排除一下,使用标记压缩法)
(5)分代算法:
思想:将内存空间根据对象的特点不同进行划分,选择合适的垃圾回收算法,以提高垃圾回收的效率。
GC过程:因为不同的对象使用的算法不一致,所以GC的过程也不一样,例如,新生代使用复制算法,老年代使用标记压缩法
优点:提高了垃圾回收的效率
应用场景:分代的思想被现有的虚拟机广泛使用,几乎所有的垃圾回收器都区分新生代和老年代。
卡表:
概念及其意义:对于新生代和老年代来说,通常新生代回收的频率很高,但是每次回收的时间都很短,而老年代回收的频率比较低,但是被消耗很多的时间。为了支持高频率的新生代回收,虚拟机可能使用一种叫做卡表的数据结构,卡表为一个比特位集合,每一个比特位可以用来表示老年代的某一区域中的所有对象是否持有新生代对象的引用,
这样一来,新生代GC时,可以不用花大量时间扫描所有老年代对象,来确定每一个对象的引用关系,而可以先扫描卡表,只有当卡表的标记为1时,才需要扫描给定区域的老年代对象,而卡表为0的所在区域的老年代对象,一定不含有新生代对象的引用。
图解:
(6)分区算法
思想:将整个堆空间划分为连续的不同小区间,每一个小区间都独立使用,独立回收。
优点:可以控制一次回收多少个小区间,通常,相同的条件下,堆空间越大,一次GC所需的时间就越长,从而产生的停顿时间就越长。为了更好的控制GC产生的停顿时间,将一块大的内存区域分割成多个小块,根据目标的停顿时间,每次合理的回收若干个小区间,而不是整个堆空间,从而减少一个GC的停顿时间。