0%

JVM-垃圾收集算法解析

垃圾收集算法

分代收集理论

当前虚拟机的垃圾收集都采用分代收集算法,这种算法没有什么新的思想,只是通过对象存活周期的不同将内存分为几块。一般将JAVA堆分为新生代和老年代 ,这样我们就可以根据各个年代的特点选择合适的垃圾手机算法。

比如在新生代,每次手机都会有99%的对象死去,所有我们可以选择复制算法,只需要付出少量对象的复制成本就可以完成每次垃圾收集。而老年代的对象存货几率是比较高的,而且没有额外的空间对它进行担保,所以我们必须选择标记-清除算法或者标记-整理算法进行垃圾收集。

注意:标记-清除算法 或 标记-整理算法 会比 复制算法 慢10倍以上!

复制算法

为了解决效率问题,复制收集算法出现了。它会将内存分为大小相同的两块,每次使用其中的一块。当这一块的内存使用完毕后,就将还存活的对象复制到另一块中,然后再把使用的空间一次性清理掉。这样就是每次的内存回收都是堆内存区域的一般进行回收。

标记-清除算法

算法分为标记清除阶段,一般分为两种模式:

  1. 标记所有存活的对象,统一回收所有未标记的对象(一般选择这种)。
  2. 标记所有需要回收的对象,统一回收所有标记的对象。

它是最基础的收集算法,但会带来两个问题:

  • 效率不高,可能出现需要标记的对象过多。
  • 空间问题,清理后出现大量的内存碎片。

标记-整理算法

根据老年代的特点设计出的一种标记算法,标记过程与标记-清楚算法一样,但后续不是直接对可回收对象回收,而是让所有存活的对象向一端移动,然后直接清理掉端边界外的内存。

垃圾收集器

各GC收集器应用范围及搭配使用图,颜色相同的表示推荐组合。

如果说垃圾收集算法是内存回收的方法论,那么垃圾收集器是就是内存回收的具体实现。

虽然我们对各个收集器进行比较,但并非为了选择出一个最好的收集器。因为直到现在为止还没有最好的垃圾收集器出现,更加没有万能的垃圾收集器,我们能做的就是根据不同的场景选择合适的垃圾收集器

Serial收集器(年轻代,串行)

开启方法:-XX:+UseSerialGC -XX:+UseSerialOldGC

Serial收集器是最基本、历史最悠久的垃圾收集器。它是一个单线程收集器,它的单线程不仅仅意味着使用一个线程进行垃圾回收,更重要的是他在进行垃圾回收时必须暂停其他所有的工作线程(Stop The World),直到它收集结束。

  • 新生代使用复制算法。
  • 老年代使用标记-整理算法。

优点:与其它手机的单线程相比它简单高效,因为它没有线程交互的开销,自然可以获得很高的垃圾收集效率。

Serial Old 收集器(老年代,串行)

Serial Old是Serial的老年代版本,它主要有两大用途:

  1. 在JDK1.5及以前的版本中与Parallel Scavenge收集器搭配使用。
  2. 作为CMS收集器的备选方案。

Parallel Scavenge(年轻代,并行,JDK8默认)

Parallel和ParallelOld是JDK8默认的垃圾收集器。

开启方法:-XX:+UseParallelGC, -XX:+UseParallelOldGC

Parallel是Serial的多线程版本,除了使用多线程进行垃圾回收外,其余行为(控制参数、收集算法、回收策略)和Serial类似。

默认的线程数和CPU核数相同,也可以用参数:-XX:ParallelGCThreads=N指定收集线程数,但一般不推荐修改。

Parallel收集器关注的重点是吞吐量,高效的使用CPU,CMS等垃圾收集器关注点更多的是工作线程的停顿时间。