Jvm 垃圾回收机制

判断一个对象是否可回收?

  • 引用计数:
    每当一个对象被引用时,其引用计数加一,当引用计数为0时GC回收内存将对象销毁。
    缺点:无法回收互相引用的对象(互相调用其引用数永远不为0)
  • 根可达算法

垃圾回收算法:

  • 标记-整理算法: 适用于回收率低的区域
    标记存活对象,将所有的存活对象压缩到内存的另一端。之后清理边界之外的所有空间。
  • 复制算法:适用于回收率高的区域
    将内存分为两半,每次只是用一半的内存,回收时将正在使用的内存中的存活的对象复制到另一半中,再清理内存。

在Hotsop虚拟机中的垃圾回收算法:

  1. 分代回收机制:将内存分为新生代和老年代,其中的新生代分为eden、幸存者区域(from和to),老年代(Old)。新生代:幸存者=8:1
  2. 在新生代中采用复制算法,每次只使用幸存者区域的一块,在回收时将eden和使用的一块幸存者区域存活的对象复制到另一块幸存者区域中,再清理,如果另一块幸存者区域放不下就进入老年代。
  3. 在老年代采用标记整理算法,将存活对象压缩内存的一边,清理另一边内存。

HotSpot启动回收时机:

  1. HotSpot采用根可达算法,在确定Root节点时需要停止所有线程的执行,在HotSpot中在解析代码时会将引用保存到一个OopMap数组中,以便虚拟机方便找到引用的区域。
  2. 在GC时不能有引用关系的变化,又不能频繁保存更新OopMap,HotSpot在代码中设置安全点,该安全点一般设置在程存活时间长的位置(如递归调用,方法跳转等),线程会跑到该安全点后挂起等待GC。
  3. 由于线程有可能存在阻塞的可能,而虚拟机不可能等待线程恢复再跑到安全点GC,所以又设置了一个安全区域(该区域中引用关系不会变化)。

垃圾回收器:

Jvm 垃圾回收机制

  • 新生代收集器:

    1. Serial收集器:新年代的Serial收集器,单线程收集器,在回收时会停止所有线程,在新生代采用复制算法。
    2. ParNew收集器:多个线程的Serial收集器,支持并行的垃圾回收(并行指的是多个线程回收,并发指的是多个线程交替进行,因此该收集器回收时用户线程还是会停止)。
    3. Parallel Scavenge收集器:吞吐量优先级的ParNew收集器,可以控制垃圾回收时间占虚拟机运行时间的百分比和控制垃圾回收时间。还可以开启自适应调节新生代区域比例功能。
  • 老年代收集器:

    1. Seril Old收集器:老年代的Serial收集器、老年代使用标记整理算法。

    2. Parallel Old收集器:Parallel Scavenge收集器的老年代版本

    3. CMS收集器:以最短收集时间为目标的收集器,能获得较快的响应速度,一般在服务器上使用,其过程分为:

      • 初始标记:标记root对象
      • 并发标记:追踪标记对象的引用
      • 重新标记:修正在并发标记时用户操作导致的变动,通过记录用户的变动记录而修改,所以时间会比并发标记快
      • 并发清除

      由于CMS采用的标记清理算法,所以会导致:

      • 回收后产生内存碎片
      • 对CPU敏感,低于4个CPU下性能不好。
      • 由于CMS需要储存空间记录用户变动和用户线程的空间并发,有可能导致GSM垃圾回收器的内存分配不足,导致full gc。
  • 通用垃圾收集器 G1: 并发的垃圾收集器,G1通过将整个对象堆分成多个Region区域,但由于多个内存区域之间会存在相互引用的存在,导致判断根可达时要扫描所有区域,所以G1按Region区域进行管理引用(通过一个Remembers Set集合),在运行到引用对象时,判断是否在同一个Region区域,否则就保存到引用所属的set中。

    • 过程:
      1. 初始标记
      2. 并发标记
      3. 最终标记
      4. 筛选回收