JVM(六)——GC 算法

标记-清除法


JVM(六)——GC 算法

概述

  GC 是 JVM 自带的功能,它可以自动回收对象,清理内存,这是 Java 语言的一大优点,可是GC毫不仅伴随着Java,相反,GC历史比Java更悠久。关于GC,我认为有四个问题须要解决:java

  • 为何了解 GC?
  • 哪些内存须要回收?
  • 何时回收?
  • 如何回收?

为何了解 GC

  GC 已经比较成熟,绝大部分状况下都“自动化”运行。之因此还须要了解GC,是由于当须要排查各类内存溢出、内存泄露问题时,当垃圾收集成为系统达到更高并发量的瓶颈时,咱们就须要对这些“自动化”的技术实施必要的监控和调节。c++

哪些内存须要回收

  断定哪些内存须要回收,不是靠 JVM 去猜,也不是随机,而是 JVM 靠一系列算法获得结果,固然,算法也是人写的,虽然不能作到百分之百地符合全部开发者的要求,但这已是最好的了。下面介绍一种断定内存回收的算法——可达性分析算法,这是我暂时能理解的算法。程序员

可达性分析算法


  这个算法的基本思路是经过一系列的称为 “GC Roots” 的对象做为起点,从这些起点开始向下搜索,搜索走过的路径称为引用链,当一个对象到 GC Roots 没有任何引用链相连时,则证实此对象是不可用的,也就是待回收的。例如,上图中的 Object 五、Object 6 虽然二者相互关联,可是它们任何一个都和 GC Roots 不可达,因此 Object 5 和 Object 6 将会被断定为可回收的对象。

GC Roots

  至于什么是 GC Roots 对象,解释以下:算法

  • 虚拟机栈(栈帧中的本地变量表)引用的对象
  • 方法区中类静态变量引用的对象
  • 方法区中常量引用的对象
  • 本地方法栈 JNI 引用的变量

何时回收

  当经过“可达性分析”等算法标记好须要回收的对象后,等待它们的不是便可问斩,而是宣告“缓刑”。最后的回收条件是此对象是否有必要执行 finalize() 方法。并发

finalize() 方法

  当对象没有覆盖 finalize() 方法,或者 finalize() 方法已经被虚拟机调用过,虚拟机都将视做“没有必要执行”。若是这个对象呗断定为有必要执行 finalize() 方法,那么这个对象将会放置在一个叫做 F-Queue 的队列中,这个队列在后期会被某个JVM自动建立的线程执行,若是一个对象在finalize()方法中执行缓慢或者发生了死循环,将可能致使 F-Queue 队列中其余对象永久处于等待状态,甚至致使整个内存回收系统崩溃,幸运的是任何一个对象的 finalize() 方法只会被系统调用一次。
  在 《深刻理解 JVM 虚拟机》中,做者建议尽可能避免使用 finalize() 方法,它不是c++ 中的析构函数,更像是java为适应c++程序员而做出的让步。我以为是finalize()方法是一根鸡肋,一根若是运用不当将会引起灾难的鸡肋,如同 goto 语句。它的运行代价极高,不肯定性大,没法保证各个对象的调用顺序。它的功能常常会被误觉得相似 try-finally,然而 finalize() 能作的工做, try-finally 都能作,并且作得更好、更及时。   函数

如何回收

标记-清除法

  “标记-清除”(Mark-Sweep)法是最基础的收集算法。算法分为两个阶段——“标记”和“清除”:首先标记出全部须要被回收的对象,在标记完成后统一回收全部标记了的对象,图如篇首。
  它有两个不足之处:高并发

  • 效率不高
      标记和清除两个过程的效率都不高
  • 产生大量不连续碎片
      虽然对象被回收,可是剩下的内存颇有可能不连续,在这种状况下,当须要为系统分配一个较大对象时,会由于没法找到足够的连续的内存而不得不提早触发另外一次垃圾收集动做。这样,又会形成效率问题

  针对上述两个缺点,后面两种算法对此进行了改进。线程

复制法

  “复制”(Copying)是为了解决“标记-清除”的效率不高问题而被发明。它将内存按容量划分为等量的两块。每次只使用其中的一块。当使用的这一块内存用完了,就将还存活的对象复制到另外一块(被称做“保留区”)上面,而后再把已使用的内存空间一次清理掉。每次都对整个半区进行回收,不再用担忧内存碎片化问题,图以下所示。3d

复制法

  可是“复制”法也有缺点,每次都将内存缩小为一半,会致使内存利用率不高。而且在对象存活率高时进行复制操做,效率就会变低(由于存活的对象要复制到“保留区”)。当赶上极端状况,对象存活率百分百(以原内存一半为单位),那就须要另外的百分之五十的空间做为分配担保。cdn

标记-整理法

  “标记-整理”(Mark-Compact)法适合对象存活率高的状况使用。“标记”过程同“标记-清除”法同样,但后续步骤不是直接对可回收对象进行清理,而是让全部存活的对象都向一端移动,而后直接清理掉端边界之外的内存,图以下所示:

标记-整理法

分代收集法

  据知,当前商业虚拟机都采用“分代收集”(Generational Collection)法,根据对象的存活周期的不一样将内存划分为几块。通常是把 Java 堆分红新生代和老年代,这样能够根据更年代的特色采用最适当的收集算法。

新生代

  在新生代中,每次垃圾收集时都会发现有大量对象死去,只有少许存活,那就选用复制算法,只须要复制少许的对象就能够完成收集,成本小。

老年代

  老年代中的对象存活率高、没有额外的空间对它进行分配担保,就必须使用“标记-清除”、和“标记-整理”法来进行回收。

相关文章
相关标签/搜索