JAVA运行时内存区域 
java垃圾收集算法

 

 

 

 

 

 

 

一、垃圾收集算法

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

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

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


算法分析

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

 

 

 

 

 

 

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

java垃圾收集算法

 


    

 

 

 


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

java垃圾收集算法

 

 

 

 

 

 

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

三、对比


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

共同点 
    当GC线程启动时(即进行垃圾收集),应用程序都要暂停(Stop The World)

 

参考文档

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

相关文章:

  • 2021-07-23
  • 2021-08-03
  • 2021-07-22
  • 2022-01-06
  • 2021-08-17
  • 2021-12-11
  • 2021-06-07
  • 2021-12-11
猜你喜欢
  • 2021-07-22
  • 2022-12-23
  • 2021-10-08
  • 2021-09-14
相关资源
相似解决方案