前言

开始跳过这节没有写,因为不太知道怎么写才条理清晰。现在整理了一下思路,在宏观上有了点脉络。垃圾回收(Garbage Collection),以下简称GC,长久以来人们一直研究的GC需要完成3件事情:

  • 1、那些内存需要回收?
  • 2、什么时候回收?
  • 3、如何回收?

那些内存需要回收?

这一块在JAVA中的基本可以转化为判断堆和方法区上哪些对象已经死去(不被任何途径使用),那些死去的对象就是需要回收的内存。
主要有两种算法:

  • 引用计数法:给对象添加一个引用计数器,每当有一个地方引用它时计数器就加1,引用失效时计数器就减1,当计数器的值为0时,该对象判为“死亡”。
    优点:实现简单,效率高
    缺点:不能解决循环引用的问题

  • 可达性分析算法:选取一些对象作为“GC root”作为起始点,然后沿着引用开始向下搜索,当一个对象对于所有“GC root”都是不可达的,那么这个对象可以判为“死亡”。(主流虚拟机采用的方式)。

什么时候回收?

  • 从虚拟机的角度来说:GC又分为 minor GC 和 Full GC (也称为 Major GC )。Java 堆内存分为新生代和老年代,新生代中又分为1个 Eden 区域 和两个 Survivor 区域。
    那么对于 Minor GC 的触发条件:大多数情况下,对象直接在 Eden 区中进行分配。如果 Eden区域没有足够的空间,那么就会发起一次 Minor GC。对于 Full GC(Major GC)的触发条件:也是如果老年代没有足够空间的话,那么就会进行一次 Full GC。
    Ps:上面所说的只是一般情况下,实际上,需要考虑一个空间分配担保的问题:
    在发生Minor GC之前,虚拟机会先检查老年代最大可用的连续空间是否大于新生代所有对象的总空间。如果大于则进行Minor GC,如果小于则看HandlePromotionFailure设置是否允许担保失败(不允许则直接Full GC)。如果允许,那么会继续检查老年代最大可用的连续空间是否大于历次晋升到老年代对象的平均大小,如果大于则尝试Minor GC(如果尝试失败也会触发Full GC),如果小于则进行Full GC。
    但是,具体到什么时刻执行,这个是由系统来进行决定,是无法预测的。
  • 从对象的角度来说:当一个对象被标记为“死亡”时,并非立即执行死刑。要真正执行死刑要经历至少两次标记。一次是在被判为“死亡”的时候,然后会进行一次筛选,筛选的方法是看是否有必要执行finalize()方法,当finalize()方法没有被覆盖或者已经被调用了则认为“没有执行的必要”。如果对象被判为有必要执行则会被放到一个叫F-Queue的队列中,然后GC会对F-Queue中的对象进行标记,这是对象逃脱GC的最后一次机会,如果在此还未逃脱,那就真的要被回收了。

如何回收?

  • 标记-清除(Mark-Sweep)算法:首先标记所有需要回收的对象,在标记完成后统一回收所有被标记的对象。
    优点:实现简单
    缺点:效率低下,会产生大量的内存碎片

  • 复制算法(Copying):将内存按容量分成两块,当这一块内存快用完了,就将还活着的对象复制到另一块去,然后再将已使用过的内存空间一次清理掉。
    优点:实现简单,运行高效,不存在碎片问题
    缺点:内存利用率低

  • 标记-整理(Mark-Compact)算法:首先标记所有需要回收的对象,在标记完成后统一整理,使其都向一端移动,然后直接清理端边界外的内存。
    优点:结合了Mark-Sweep和Copying的优点
    缺点:算法实现较为复杂

  • 分代收集算法:根据对象生存周期的不同对内存进行划分。一般将Java堆划成新生代和老年代,这样就可以根据各个年代的特点采用最合适的垃圾收集算法。比如新生代对象”朝生夕死”的情况比较多,可以采用复制算法,老年代对象存活率高,可以采用标记-清除或标记-整理算法。

垃圾回收器

这里不做过多讲解,放一JDK1.7Update14后的HotSpot虚拟机包含的Java虚拟机图示:

其中注意G1收集器是当前收集器技术的最前沿成果之一