GC回收之垃圾收集算法

    JAVA运行时内存区域 

一,垃圾收集算法

GC管理的主区域是Java堆,一般情况下只针对堆进行垃圾回收。方法区,栈,本地方法区不被GC所管理,因而选择这些区域内的对象作为GC根,被GC根引用的对象不被GC回收。
.GC(垃圾收集器)根,特指的是垃圾收集器的对象,GC会收集那些不是GC根和没有被GC根引用的对象。

一般的可标记为存活对象的是

1.虚拟机栈中引用的对象 。
2.本地方法栈中天然方法引用的对象 。
3.方法区中静态属性引用的变量 。
4.方法区中常量引用的对象。


算法分析

1.标记 - 清除算法(Mark-Sweep)
    应用场景:   针对老年代的CMS收集器;
    思路:
        标记:遍历获取所有的GC Roots,然后将所有GC Roots可达的对象标记为存活对象。
        清楚:遍历堆中所有的对象,将没有标记的对象全部清除掉。
    优点:
       基于最基础的可达性分析算法,它最基础的收集算法。
       后续的收集算法都是基于这种思路并对其不足进行改进的得到。
    缺点:
        效率问题,标记和清除过程的效率都不高。
        空间问题,标记清除之后会产生大量不连续的内存碎片【空间碎片太多可能会导致大对象无法分配到足够的连续内存,从而不得不提前触发GC,甚至停止世界。
    

 
2.复制算法(复制)一般用于新生代
    应用场景:如串行收集器,ParNew收集器,Parallel Scavenge收集器, G1(从局部看)
    优化标记/ 清除算法的效率低,内存碎片多的问题。
    思路:
        (A)内存按容量划分为大小相等的两块,每次使用其中的一块。
        (B)当一块内存用完了,就将还存活的对象复制到另一块上(而后使用这一块)。
        (C)再把已使用过的那块内存空间一次清理掉,而后重复步骤B。
    优点:
        这使得每次都是只对整个半区进行内存回收。
        内存分配时也不用考虑内存碎片等问题(可使用“指针碰撞”的方式分配内存)。
        实现简单,运行高效。
    缺点:
        空间浪费[将内存缩小为原来的一半,浪费了一半的内存空间,代价太高【解决:可以改良,不按1:1划分比例】。
        效率随对象存活率升高而变低【当对象存活率较高时,需要进行较多复制操作,效率将会变低(解决:后面 标记-整理算法)】。


 
3,标记 - 整理算法(Mark-Compact)一般用于老年代
    应用场景:部分一般收集器采用这种算法来回收老年代,如Serial Old收集器,G1(从整体看)产生。
    :复制算法在对象存活率较高时就要执行较多的复制操作,效率将会变低。以及50%的空间浪费。
    思路:
        标记:标记过程与“标记 - 清除”算法一样;
        整理:让所有存活的对象都向一端移动,按照内存地址次序依次排列,然后直接清理掉端边界[GCRoot引用到的存活对象]以外的内存。【所有现代的标记 - 整理回收器均使用滑动整理】
    优点:
        不会像复制算法,效率随对象存活率升高而变低。
        不会像标记 - 清除算法,产生内存碎片。
        可以解决循环引用的问题。
        必要时才回收(内存不足时) 
    缺点:
        回收时,应用需要挂起,也就是阻止世界(stop-the-world)【停止应用程序,会这导致用户体验非常差】
        清理出来的空闲内存不是连续 。
        除像标记-清除算法的标记过程外,还多了需要整理的过程,效率更低。

4,分代收集算法[像是调度员]
    应用场景:目前几乎所有商业虚拟机的垃圾收集器都采用分代收集算法。[Serial,Serial Old,ParNew,Parallel Scavenge,Parallel Old,CMS]产生
    结合不同的收集算法处理不同区域。
    思路:
        根据对象存活周期的不同将内存划分为几块。
        根据各个年代的特点采用最适当的收集算法;
    优点:
        可以根据各个年代的特点采用最适当的收集算法;
    缺点:
         不能控制还是垃圾每次收集的时间
    补充:
        在新生代中,存活对象少,存活率低于是选用复制算法,需要只付出少量存活对象的复制成本就可以完成收集
        在老年代中,存活对象多,存活率高。于是选用标记 - 清理“或”标记 - 整理“

三,对比


    效率:复制算法>标记/整理算法>标记/清除算法(标记/清除算法有内存碎片问题,给大对象分配内存时可能会再次出发垃圾回收)
    内存整齐率:复制算法=标记/整理算法>标记/ 清除算法
    内存利用率:标记/整理算法=标记/清除算法>复制算法
    
    
结果:标记/清除算法已经是比较落后了,不过作为最基础的收集算法!还是有地方可以采用的
      目前最好的算是分代收集算法了!毕竟几乎所有商业虚拟机的垃圾收集器都采用了。结合前三个算法的优点,将算法组合使用进行垃圾回收!
      分代收集算法的原理是采用复制算法来收集新生代,采用标记/清理算法或者标记/整理算法收集老年代。

共同点 
    当GC线程启动时(即进行垃圾收集),应用程序都要暂停(stop-the-world

参考文档

https://blog.csdn.net/clover_lily/article/details/80160726

猜你喜欢

转载自blog.csdn.net/qq_35649135/article/details/83894237
今日推荐