beizili

  博客园  :: 首页  :: 新随笔  :: 联系 :: 订阅 订阅  :: 管理

 消失的垃圾——垃圾回收算法

JAVA和C++之间有一堵由内存动态分配垃圾自动回收的高墙。正所谓,城里的人想出来,城外的人想进去,只因为这两种方式各有利弊。如果更好的使用java,需要我们了解java的垃圾回收机制。

今天我们来聊一聊jvm的垃圾回收算法的思想。

1、垃圾回收算法的分类:

垃圾回收算法,从判定对象消亡的角度上面分类,可以分为“引用计数式垃圾收集”和“追踪式垃圾收集”。主流的java虚拟机用到的大部分都是“追踪式垃圾收集”。

2、标记——清除算法:

首先标记出所有需要回收的对象,在标记完成后,统一回收掉所有被标记的对象(也可以反过来,标记存活的对象,统一回收未被标记的对象)。

缺点:

  1. 执行效率不稳定。如果java堆中包含大量对象,而且其中大部分是需要被回收的,这是必须进行大量标记和清除的动作,导致标记和清除两个过程执行效率都随着对象数量的增长而降低。
  2. 内存空间碎片化。标记和清除后会产生大量不连续的内存碎片,空间碎片太多可能会导致当以后在程序运行过程中需要分配较大对象时无法找到足够的连续内存而不得不提前触发另一次垃圾收集动作。

3、标记——复制算法:

为了解决标记清除算法的缺点,人们提出了标记复制算法,即将内存容量分成大小相等的两块,每次只是用其中一半。当一半内存用完了之后,把存活的对象复制到另一块上面,并把原来的一半一次清理掉。

这种做法,在存活对象较多的时候,效率比较低。但是当存活对象比较少时实现简单,运行高效。不过有一个明显的缺点,就是这种垃圾回收算法让jvm减少了一半。因此,有了更好的标记复制算法。

新生代的对象都具有“朝生夕死”的特点,即新生代中的对象有98%熬不过第一轮垃圾回收,因此内存比例不应该使用1:1来划分。具体的做法:将新生代分为一块交大的Eden和两块较小的相等的Survivor。每次分配内存只使用Eden和其中一块Survivor。发生垃圾收集时,将Eden和Survivor中存活的对象一次性复制到另外一个Survivor,然后直接去清理掉Eden和已经用过的Survivor。如果另外一块Survivor没有足够的空间存放,则这些存活对象直接进入老年代。

hotSpot虚拟机默认Eden:Survivor=8:1。-XX:SurvivorRatio=n:年轻代中Eden区与两个Survivor区的比值。注意Survivor区有两个。如:3,表示Eden:Survivor=3:2,一个Survivor区占整个年轻代的1/5。

4、标记——整理算法

上面说到标记复制算法,在存活对象较多是,效率比较低。而老年代正是有:存活对象较多,的这么一个特点,显然标记复制算法不适合老年代。于是有了标记整理算法,用于对标记清除算法进行优化。

标记整理算法和标记清除算法的区别就在于前者是移动式的,即在使用跟标记清除算法一个的标记阶段之后,不是直接回收,而是把所有存活对象都向内存空间的一端移动。

缺点:

  1. 像老年代这种存活对象比较多的内存区域,每次移动对象都是一次极为负重的操作。
  2. 对象移动需要暂停应用程序,即“stop the world”。

整理有整理的缺点,不整理的标记清除算法,也有上面讲到的缺点,总之开发者要权衡利弊。

这边针对老年代,就有一种垃圾收集器——CMS垃圾收集器。CMS在平时大多数时间使用标记清除算法,当空间碎片过多时,使用标记整理算法。

 

更多内容请关注微信公众号“外里科技

官方公众号外里科技
运营公众号英雄赚
微信wxid_8awklmbh1fzm22
QQ1247408032
开源代码https://gitee.com/B_T/beimi

 

posted on 2020-07-12 23:55  被子里  阅读(1)  评论(0编辑  收藏  举报  来源