问:
讲讲你知道的垃圾回收算法?
提示:以下是本篇文章正文内容,下面案例可供参考
一、标记-清除
最基础的收集算法是“标记-清除”(Mark-Sweep)算法,如同它的名字一样,算法分为“标记”和“清除”两个阶段:首先标记出所有需要回收的对象,在标记完成后统一回收所有被标记的对象,它的标记过程其实在前一节讲述对象标记判定时已经介绍过了。之所以说它是最基础的收集算法,是因为后续的收集算法都是基于这种思路并对其不足进行改进而得到的。
不足:
一个是效率问题,标记和清除两个过程效率都不高;
另一个是空间问题,标记清除之后会产生大量不连续都内存碎片,空间碎片太多可能会导致以后在程序运行过程中需要分配较大对象时,无法找到足够都连续内存而不得不提前触发另一次垃圾收集动作。
二、复制算法
从根集合节点进行扫描,标记出所有的存活对象,并将这些存活的对象复制到一 块儿新的内存(图中下边的那一块儿内存)上去,之后将原来的那一块儿内存(图 中上边的那一块儿内存)全部回收掉。
现在的商业虚拟机都采用这种收集算法来回收新生代。
缺点:
- 需要一块空的内存空间
- 需要复制移动对象
三、标记-整理
复制收集算法在对象存活率较高时就要进行较多的复制操作,效率将会变低。更关键的是,如果不想浪费50%的空间,就需要有额外的空间进行分配担保,以应对被使用的内存中所有对象都100%存活的极端情况,所以在老年代一般不能直接选用这种算法。
根据老年代的特点,选择标记-整理(Mark-Compact)算法,标记过程仍然与“标记-清除”算法一样,但后续步骤不是直接对可回收对象进行清理,而是让所有存活对对象都向一端移动,直接清理掉指针以外都内存对象。
四、分代收集算法
目前虚拟机使用的回收算法,它解决了标记整理不适用于老年代的问题,将内存分为各个年代。一般情况下将堆区划分为老年代(Tenured Generation)和新生代(Young Generation),在堆区之外还有一个代就是永久代(Permanet Generation)。
在不同年代使用不同的算法,从而使用最合适的算法,新生代存活率低,可以使 用复制算法。而老年代对象存活率高,没有额外空间对它进行分配担保,所以只 能使用标记清除或者标记整理算法。
垃圾回收机制
年轻代分为 Eden 区和 survivor 区(两块儿:from 和 to),且 Eden:from:to==8:1:1
jvm 内存结构
-
新产生的对象优先分配在 Eden 区(除非配置了-XX:PretenureSizeThreshold, 大于该值的对象会直接进入年老代);
-
当 Eden 区满了或放不下了,这时候其中存活的对象会复制到 from 区。这里,需要注意的是,如果存活下来的对象 from 区都放不下,则这些存活下来的对象全部进入年老代。之后 Eden 区的内存全部回收掉。
-
之后产生的对象继续分配在 Eden 区,当 Eden 区又满了或放不下了,这时候 将会把 Eden 区和 from 区存活下来的对象复制到 to 区(同理,如果存活下来的对象 to 区都放不下,则这些存活下来的对象全部进入年老代),之后回收掉 Eden 区和 from 区的所有内存。
-
如上这样,会有很多对象会被复制很多次(每复制一次,对象的年龄就+1), 默认情况下,当对象被复制了 15 次(这个次数可以通过: -XX:MaxTenuringThreshold 来配置),就会进入年老代了。
-
当年老代满了或者存放不下将要进入年老代的存活对象的时候,就会发生一 次 Full GC(这个是我们最需要减少的,因为耗时很严重)。
公共号专注于IT测试-开发领域干货分享,希望陪同有梦想的你一同成长~
从测试到开发,从面试秘籍到技术管理,还有读书分享及职场经验。