JVM之GC算法

JVM之GC算法

GC对与大家来说都不陌生,它是Java中的垃圾回收机制。用来回收那些不用的对象或者是没有意义的对象,比如说你定义了一个空对象或者没有使用这个引用对象,GC扫描到这些没用的对象就会清理掉。那么问题来了?如何确定这些不用的对象是需要回收的、在什么时候回收、采用什么方式回收。

1.判断该对象是否是要清理的对象,有两种方法可以去判断。

1. 引用计数算法

对象在创建的时候,会在这个对象里面添加一个计数器。计数器默认值是0则表示该对象没有被引用,这个对象被使用了则计数器加一,引用失败则减一。计数器为0的则是要被回收的对象。引用计数算法有一个致命缺陷,对象循环引用,下面写一个例子

他们的引用没有任何意义,但计数器的值不是0,就不能判断它是可收回对象。所以又采用了可达性分析算法来解决这个问题。

2. 可达性分析算法

这种算法有效的避免了对象循环引用情况,整个对象实例以一个树的形式展现出来。根节点是一个"GCRoots"的对象,从这个对象开始向下搜索并作标记,遍历完这棵树过后,未被标记的对象就会判断要被收回的对象。如下图所示:

Object4和Object5就是未标记的要被回收的。

2.采用什么方式去回收

垃圾收集算法有三种。

2.1 标记-清除算法

分为两个阶段:

2.11 标记阶段

遍历所有的GCRoots,然后将所有GCRoots可达的对象标记为存活的对象。

如图所示:0为未标记,1为标记


2.22 清理阶段

先遍历堆中所有的对象,将没有标记的对象全部清除。

清除之后之前被标记的对象将重新归为0,如下图所示:


缺点:1.效率低每次都要遍历出根节点下面的所有对象。2.在回收时,应用会被停止。3.会造成内存碎片,会导致明明有内存空间,由于不连续,而导致不能使用。申请稍微大点的对象则无法使用。

2.2 复制法

为了解决内存碎片,这里就引进了复制算法。首先把内存分为两块,一个是活动区域,另一个是空闲区域。当有效内存空间耗尽时,JVM将暂停程序运行,开始复制算法GC线程。接下来GC线程会将活动区间内的存活对象,全部复制到空闲区间,下一步将不用回收的内存复制到新的内存区域,这样旧的内存区域就可以全部进行回收,而新的内存区域则是连续的。

如图所示:


其实这个图依然是上一章的例子,只不过此时内存被复制算法分成了两部分,下面我们看下当复制算法的GC线程处理之后,两个区域会变成什么样子,如下所示。

可以看到,1和4号对象被清除了,而2、3、5、6号对象则是规则的排列在刚才的空闲区间,也就是现在的活动区间之内。此时左半部分已经变成了空闲区间,不难想象,在下一次GC之后,左边将会再次变成活动区间。很明显,复制算法弥补了标记/清除算法中,内存布局混乱的缺点。不过与此同时,它的缺点也是相当明显的。

2.3 标记-整理法

分为两个阶段:

2.3.1 标记阶段

它的第一个阶段与标记/清除算法是一模一样的,均是遍历GC Roots,然后将存活的对象标记

如下图所示:0未标记,1标记


2.3.2 整理阶段

移动所有存活的对象,且按照内存地址次序依次排列,然后将末端内存地址以后的内存全部回收。因此,第二阶段才称为整理阶段。如图所示:把末端的要回收的对象回收掉,把存活的对象的标记重新归0。



猜你喜欢

转载自blog.csdn.net/demo_gsl/article/details/80895209