原文链接

回收的是什么?
答:运行程序中没有任何指针援用的对象,这个对象就是须要被回收的垃圾

垃圾回收算法

标记阶段

  • 在GC执行垃圾回收之前,首先须要辨别出内存中那些是存活的对象,那些是曾经死亡的对象。只有被标记为已死亡的对象,GC才会在执行垃圾回收时,开释掉其存活所占用的内存空间。此过程被称为垃圾标记阶段
  • 当一个对象曾经不再被任何的存活对象持续援用时,就能够宣判为曾经死亡
  • 判断对象存活的有两种形式:援用计数算法可达性剖析算法

援用计数算法(Reference Counting)

  • 形容:对每一个对象保留一个整型的援用计数器属性,用于记录对象被援用的的状况
  • 长处:

    • 实现简略,垃圾对象便于辨识
    • 断定效率高,回收没有提早性
  • 毛病:

    • 须要独自的字段存储计数器,减少了存储空间
    • 每次赋值都须要从新更新计数器,减少了工夫开销
    • 无奈解决循环援用

可达性剖析算法

  • 解决了援用计数算法中循环援用的问题,避免内存泄露
  • 可达性剖析算法是以根对象汇合(GC Roots)为起始点,依照从上至下的形式搜寻被根对象汇合所连贯的指标对象是否可达
  • 应用该算法后,内存中的存活对象都会被根汇合间接或间接的连贯着,搜寻走过的门路称为援用链
  • 如果没有任何援用链,则不可达,阐明对象已死亡,能够标记为垃圾对象

GC Roots蕴含以下几类元素:

  1. 虚拟机栈中援用的对象
  2. 本地办法(native办法)中援用的对象
  3. 办法区中常量援用的对象
  4. 被同步锁synchronized持有的对象
  5. Java虚拟机外部的援用

    • 根本数据类型对应的Class对象常驻异样对象和零碎类加载器
  6. 反映Java虚拟机外部状况的JMXBean、 JVMTI中注册的回调、本地代码缓存等

finalization机制

  • 提供对象被销毁之前的自定义解决逻辑
  • 当垃圾回收器发现没有援用指向一个对象,总会先调用这个对象的finalize()办法
  • finalize()办法容许在子类中被重写,用于在对象被回收时进行资源开释。例如:敞开文件,断开数据库连贯

革除阶段

当胜利辨别出内存中存活对象的和死亡对象后,接下来就会执行垃圾回收,开释掉无用对象所占用的内存空间,以便有足够的空间为新对象所调配

标记-革除算法(Mark-Sweep)

执行过程:当堆中的内存空间被耗尽的时候,就会进行整个程序(stop the world),而后进行标记和革除工作。

标记:Collector从援用根节点开始遍历,标记所有被援用的对象,并将这些对象记录在Header中。(找出在程序中被援用的对象,并革除其余的)

革除:Collector从堆内存中从头到尾的进行线性遍历,如果发现某个对象在Header中没有标记为可达对象,则将其回收。(将不可达对象寄存在闲暇列表中,当有新对象进入时,将其地位笼罩)

毛病:

  • 效率不高
  • 在进行GC时,须要进行整个应用程序,导致用户体验差
  • 清理进去的闲暇内存是不间断的,产生内存碎片且须要保护一个闲暇列表

复制算法(Copying)

执行过程:当A空间同时存在可达和不可达对象时,将可达对象复制到B空间并保障空间的间断,后革除A空间的所以对象

长处:

  • 没有标记革除过程,实现简略
  • 将存在援用关系的对象复制过来后,保障空间的连续性,不会存在碎片

毛病:

  • 须要两倍的内存空间
  • 对于存活对象的数量远大于垃圾对象数量时,此革除算法效率差。

标记-压缩算法(Mark-Compact)

执行过程:

  1. 从根节点开始标记存在援用关系的对象
  2. 将这些对象压缩到内存的一端并按程序排放
  3. 革除边界外的所以对象

长处:

  • 对于“标记-革除算法”,因为应用了压缩性能,当有新对象分配内存时,JVM只须要持有一个内存的起始地址即可
  • 对于“复制算法”,缩小了内存空间的占用

毛病:

  • 比照“复制算法”,效率低
  • 在挪动存在援用关系的对象时,须要调整援用的地址
  • 挪动过程中,须要全程暂停用户程序(stop the world)

分代收集算法

依据年老代和老年代的特点应用不同的内存回收算法

年老代:

针对年老代空间小,对象生命周期短,收集频繁和存活率低的特点,应用复制算法能够高效实现内存回收,复制算法使用率不高的问题,通过两个survivor的设计能够无效的缓解。

老年代:

老年代是空间大,对象生命周期长,存活率高和回收不频繁,应用标记-革除算法或标记-革除算法和标记-压缩算法联合实现内存空间的回收。

finalize

内存溢出 和内存泄露