阅读 298

GC算法和种类

GC的概念

 Garbage Collection 垃圾收集,简称GC。Java中GC的对象是堆空间和永久区。
复制代码

GC算法

引用计数法
  • 概念:引用计数法是老牌的垃圾回收算法了,通过对象的引用来计算要不要回收,com、ActionScript3、python 都使用过。
  • 原理:对于一个对象A,只要任何一个对象引用,则A的引用计数器就加1,如果取消引用就减一。如果计数器为零这个对象就是垃圾
  • 缺点:引用和去除引用伴随着加法减法,影响性能。还有就是很难处理循环引用(看下图)。
  • 优点:即时回收,因为对象知道自己什么时候没用
标记清除
  • 概念:标记——清除算法是现代垃圾回收算法的思想基础。
  • 原理:这个算法将垃圾回收分为了两个阶段,标记阶段和清除阶段。一种可行的实现是,在标记阶段,首先通过根节点,标记所有从根节点开始可达对象。因此未标记的对象就是未被引用的垃圾对象。然后在清除阶段,清除未被标记的对象。(看下图)
  • 缺点:逐渐产生被细化的分块,不久后就会导致无数的 小分块散布在堆的各处。这个内存就会被分的七零八落的,这样在分配对象的时候还得先遍历那些内存块可以用。
  • 优点:实现起来简单
标记压缩
  • 概念:标记-压缩算法适合用于存活对象较多的场合,如老年代。它在标记-清除算法的基础上做了一些优化
  • 原理:和标记清除一样,先标记但是之后清除操作的时候将所有存活的对象压缩到内存的一端,然后清理这个边界外的所有空间
  • 缺点:清除算法中,清除阶段也要搜索整个堆,不过搜索 1 次就够了。但 GC 标记 - 压缩算法要搜索 3 次,这样就要花费约 3 倍的时间,这是一个相当巨大的缺陷,特别是堆越 大,所消耗的成本也就越大(看图)
  • 优点:堆利用效率高。 而且 GC 标记 - 压缩算法不会出现 GC 复制算法那样只能利用半个堆的情况
复制算法
  • 概念:在标记清除的的基础上改进

  • 原理:将原有的内存空间分为两块,每次只使用其中一块,在垃圾回收时,将正在使用的内存中的存活对象复制到未使用的内存块中,之后,清除正在使用的内存块中的所有对象,交换两个内存的角色,完成垃圾回收

  • 缺点:看图

  • 优点:

    1、优秀的吞吐量: GC 标记 - 清除算法消耗的吞吐量是搜索活动对象(标记阶段)所花费的时间和搜索整体 堆(清除阶段)所花费的时间之和。  
     另一方面,因为 GC 复制算法只搜索并复制活动对象,所以跟一般的 GC 标记 - 清除算 法相比,它能在较短时间内完成 GC。也就是说,其吞吐量优秀。
    
    2、高速分配:GC 复制算法不使用空闲链表。这是因为分块是一个连续的内存空间。比起 GC 标记 - 清除算法和引用计数法等使用空闲链表的分配,GC 复制算法明显快得多。
    
    3、不会发生碎片化
    复制代码
GC算法总结

总结的时候我觉得还是引入一下分代思想比较清晰

依据对象的存活周期进行分类,短命对象归为新生代,长命对象归为老年代。

根据不同代的特点,选取合适的收集算法

  • 少量对象存活,适合复制算法
  • 大量对象存活,适合标记清理或者标记压缩

可触及性

问:GC算法中 怎么判断这个对象是否是垃圾对象,是根据可触及性来判断的,那么什么是可触及性呢。

答:

  • 栈中引用的对象
  • 方法区中静态成员或者常量引用的对象(全局对象)
  • JNI方法栈中引用对象

还有一个概念也不能判定这个对象是垃圾对象就是 --可复活的

  一旦所有引用被释放,就是可复活状态,因为在finalize()中可能复活该对象
复制代码

不过这个一般不使用:

 避免使用finalize(),操作不慎可能导致错误。
 优先级低,何时被调用, 不确定
 何时发生GC不确定
 可以使用try-catch-finally来替代它经验:避免使用finalize(),操作不慎可能导致错误。 
 优先级低,何时被调用, 不确定
 何时发生GC不确定
 可以使用try-catch-finally来替代它
复制代码

这里你可以代码试下

public class CanReliveObj {
   public static CanReliveObj obj;
   @Override
   protected void finalize() throws Throwable {
       super.finalize();
       System.out.println("CanReliveObj finalize called");
       obj=this;
   }
   @Override
   public String toString(){
       return "I am CanReliveObj";
   }


public static void main(String[] args) throws
    InterruptedException{
obj=new CanReliveObj();
obj=null;   //可复活
System.gc();
Thread.sleep(1000);
if(obj==null){
   System.out.println("obj 是 null");
}else{
   System.out.println("obj 可用");
}
System.out.println("第二次gc");
obj=null;    //不可复活
System.gc();
Thread.sleep(1000);
if(obj==null){
System.out.println("obj 是 null");
}else{
System.out.println("obj 可用");
}
}
复制代码

Stop-The-World

详情点击 juejin.im/post/684490…

Java中一种全局暂停的现象,全局停顿,所有Java代码停止,native代码可以执行,但不能和JVM交互,多半由于GC引起 Dump线程、 死锁检查、堆Dump。

GC时为什么会有全局停顿? 类比在聚会时打扫房间,聚会时很乱,又有新的垃圾产生,房间永远打扫不干净,只有让大家停止活动了,才能将房间打扫干净。

危害:长时间服务停止,没有响应,遇到HA系统,可能引起主备切换,严重危害生产环境。