JVM垃圾回收机制总结(2) :基本算法概述

系统 1347 0

1、引用计数收集器 (Reference Counting)

 

      引用计数是垃圾收集的早期策略。在这种方法中, 堆中每一个对象都有一个引用计数。一个对象被创建了,并且指向该对象的引用被分配给一个变量,这个对象的引用计数被置为1。当任何其他变量被赋值为对这个对象的引用时,计数加1。当一个对象的引用超过了生存期或者被设置一个新的值时,对象的引用计数减1。任何引用计数为0的对象可以被当作垃圾收集。当一个对象被垃圾收集的时候,它引用的任何对象计数值减1。 在这种方法中,一个对象被垃圾收集后可能导致后续其他对象的垃圾收集行动。
      这种方法的好处是,引用计数收集器可以很快地执行,交织在程序的运行之中。这个特性对于程序不能被长时间打断的实时环境很有利。坏处就是,引用计数无法检测出循环(即两个或者更多的对象互相引用)。 循环的例子如,父对象有一个对子对象的引用,子对象又反过来引用父对象。这些对象永远都不可能计数为0,就算它们已经无法被执行程序的根对象可触及。还有一个坏处就是,每次引用计数的增加或者减少都带来额外开销。
      因为引用计数方法固有的缺陷,这种技术现在已经不为人所接受。现实生活中所遇到的Java虚拟机更有可能在垃圾收集堆中使用追踪算法。

 

2、标记-清除收集器(Mark-Sweep)或追踪回收器( Tracing Collector

 

       跟踪收集器追踪从根结点开始的对象引用图。在追踪过程中遇到的对象以某种方式打上标记。 总的来说,要么在对象本身设置标记,要么用一个独立的位图来设置标记。 当追踪结束时,未被标记的对象就知道是无法触及的,从而可以被收集。
      基本的追踪算法被称作“标记并清除”。这个名字指出垃圾收集过程的两个阶段。在标记阶段,垃圾收集器遍历引用树,标记每一个遇到的对象。在清除阶段,未被标记的对象被释放了,使用的内存被返回到正在执行的程序。在Java虚拟机中,清除步骤必须包括对象的终结。下面的图示显示了这种收集器的收集过程:

 

垃圾回收器回收了堆中的对象之后,自然会在堆内存中遗留下一些内存空闲的间隙,下面就是完成上图所示的标记-清除垃圾回收之后的堆内存变化,我们能很清楚的看到这一点。

 

下面我们就要讨论如何对付这些回收对象后产生的内存碎块的垃圾回收器。

 

3、基于标记-清除的压缩收集器

      压缩方法把活动的对象越过空闲区滑动到堆的一端,在这个过程中,堆的另一端出现一个大的连续空闲区。所有被移动的对象的引用也被更新,指向新的位置。
      更新被移动的对象的引用有时候通过一个间接对象引用层可以变得更简单。不直接引用堆中的对象,对象的引用实际上指向一个对象句柄表。对象句柄才指向堆中对象的实际位置。当对象被移动了,只有这个句柄需要被更新为新位置。所有的程序中对这个对象的引用仍然指向这个具有新值的句柄,而句柄本身没有移动。这种方法简化了消除堆碎块的工作,但是每一次对象访问都带来了性能损失。

 

4、拷贝收集器

      一般的拷贝算法被称为“停止并拷贝”、,在这个方案中,堆被分为两个区域,任何时候都只使用其中的一个区域。对象在同一个区域中分配,直到这个区域被耗尽。此时,程序执行被中止、堆被遍历,遍历时遇到的活动对象被拷贝到另外一个区域。当停止和拷贝过程结束时。程序恢复执行。内存将从新的堆区域中分配,直到它也被用尽。那时程序将再次中止。遍历堆、活动对象又被拷贝回原来的区域‘这种方一法带来的代价就是,对于指定大小的堆来说需要两倍大小的内存,因为任何时候都只能使用其中的一半。下图就是一个停止并拷贝的过程:

 

5、按代收集器(Generational Collector) 

      简单的停止并拷贝收集器的缺点是每一次收集时,所有的活动对象都必须被拷贝。大部分语言的大多数程序都有以下特点,如果我们全面考虑这些,拷贝算法的这个缺点足可以被改进的。

      (1) 大多数程序创建的大部分对象都具有很短的生命期。
      (2)大多数程序都创建一些具有非常长生命周期的对象。

      简单的拷贝收集器浪费效率的一个上要原因就是、它们每次都把这些生命周期很长的对象来回拷贝,消耗大量时间。   

      按代收集的收集器通过把对象按照寿命来分组解决这个效率低下的问题,更多地收集那些短暂出现的年幼对象,而非寿命较长的对象。在这种方法里,堆被划分成两个或者更多的子堆,
每一个子堆为一“代”对象服务。最年幼的那一代进行最频繁的垃圾收集。因为大多数对象都是短促出现的,只有很小部分的年幼对象可以在它们经历第一次收集后还存活。如果一个最年幼的对象经历了好几次垃圾收集后仍然存活,那么这个对象就成长为寿命更高的一代:它被转移到另外一个子堆中去。年龄更高的每一代的收集都没有年径的那一代来得频繁。每当对象在它所属的年龄层(代)中变得成熟(逃过了多次垃圾收集)之后,它们就被转移到更高的年龄层中去。

      按代进行的收集技术除了可以应用于拷贝算法,也可以应用于标记并清除算法。不管在哪种情况下,把堆按照对象年龄层分解都可以提高最基本的垃级收集算法的性能。

 

 

总结

      引用计数收集器 基本上已经不被使用了。 标记-清除收集器 是目前寻找垃圾对象的主流策略,但是它对回收后产生的内存碎片却无能为力,因此我们必须采用一些合并空闲内存的算法,这就产生了基于标记-清除的 压缩收集器 拷贝收集器 。而基于性能原因,拷贝收集器是比较受欢迎的。但是它有一个比较大的弱点:对于寿命较长的对象会不停的拷贝,这就付出了不必要的代价。因此, 按代收集器 的出现 改进了这一性能。而我们的JDK中的JVM就采用了按代收集策略。   

 

 

垃圾回收的若干问题


(1) 垃圾回收从哪里开始??

 

      上面说到的“引用计数”法,通过统计控制生成对象和删除对象时的引用数来判断。垃圾回收程序收集计数为0的对象即可。但是这种方法无法解决循环引用。所 以,后来实现的垃圾判断算法中,都是从程序运行的根节点出发,遍历整个对象引用,查找存活的对象。那么在这种方式的实现中, 垃圾回收从哪儿开始的呢 ? 即,从哪儿开始查找哪些对象是正在被当前系统使用的。上面分析的堆和栈的区别,其中栈是真正进行程序执行地方,所以要获取哪些对象正在被使用,则需要从 Java栈开始。同时,一个栈是与一个线程对应的,因此,如果有多个线程的话,则必须对这些线程对应的所有的栈进行检查。

       同时,除了栈外,还有系统运行时的寄存器等,也是存储程序运行数据的。这样,以栈或寄存器中的引用为起点,我们可以找到堆中的对象,又从这些对象找到对 堆中其他对象的引用,这种引用逐步扩展,最终以null引用或者基本类型结束,这样就形成了一颗以Java栈中引用所对应的对象为根节点的一颗对象树,如 果栈中有多个引用,则最终会形成多颗对象树。在这些对象树上的对象,都是当前系统运行所需要的对象,不能被垃圾回收。而其他剩余对象,则可以视为无法被引 用到的对象,可以被当做垃圾进行回收。

        因此, 垃圾回收的起点是一些根对象(java栈, 静态变量, 寄存器...) 。而最简单的Java栈就是Java程序执行的main函数。这种回收方式,也是上面提到的“标记-清除”的回收方式。

(2) 如何处理碎片??

   由于不同Java对象存活时间是不一定的,因此,在程序运行一段时间以后,如果不进行内存整理,就会出现零散的内存碎片。碎片最直接的问题就是会导致无法 分配大块的内存空间,以及程序运行效率降低。所以,在上面提到的基本垃圾回收算法中, 基于标记-清除的压缩收集器和拷贝收集器 ,都可以解决碎片的问题。

(3) 如何解决同时存在的对象创建和对象回收问题??

      垃圾回收线程是回收内存的,而程序运行线程则是消耗(或分配)内存的, 一个回收内存,一个分配内存 ,从这点看,两者是矛盾的。因此,在现有的垃圾回收方式中,要进行垃圾回收前,一般都需要暂停整个应用(即:暂停内存的分配),然后进行垃圾回收,回收完成后再继续应用。这种实现方式是最直接,而且最有效的解决二者矛盾的方式。

      但是 这种方式有一个很明显的弊端,就是当堆空间持续增大时,垃圾回收的时间也将会相应的持续增大,对应应用暂停的时间也会相应的增大 一些对相应时间要求很高的应用,比如最大暂停时间要求是几百毫秒,那么当堆空间大于几个G时,就很有可能超过这个限制,在这种情况下,垃圾回收将会成为系统运行的一个瓶颈。为解决这种矛盾,有了 并发垃圾回收算法 ,使用这种算法,垃圾回收线程与程序运行线程同时运行。在这种方式下,解决了暂停的问题,但是因为需要在新生成对象的同时又要回收对象,算法复杂性会大大增加,系统的处理能力也会相应降低,同时,“碎片”问题将会比较难解决。

 

JVM垃圾回收机制总结(2) :基本算法概述


更多文章、技术交流、商务合作、联系博主

微信扫码或搜索:z360901061

微信扫一扫加我为好友

QQ号联系: 360901061

您的支持是博主写作最大的动力,如果您喜欢我的文章,感觉我的文章对您有帮助,请用微信扫描下面二维码支持博主2元、5元、10元、20元等您想捐的金额吧,狠狠点击下面给点支持吧,站长非常感激您!手机微信长按不能支付解决办法:请将微信支付二维码保存到相册,切换到微信,然后点击微信右上角扫一扫功能,选择支付二维码完成支付。

【本文对您有帮助就好】

您的支持是博主写作最大的动力,如果您喜欢我的文章,感觉我的文章对您有帮助,请用微信扫描上面二维码支持博主2元、5元、10元、自定义金额等您想捐的金额吧,站长会非常 感谢您的哦!!!

发表我的评论
最新评论 总共0条评论