垃圾收集器与内存分配

一.判断对象是否存活的算法:

  1. 引用计数法:

    给对象添加一个引用计数器,每一个地方引用它时计数器加1;引用失效时计数器减1.为值为0的对象不可能再被使用.

    缺点:

    难以解决对象之间循环引用的问题(对象A引用对象B,对象B引用对象A,两个对象无实际意义)

  2. 可达性分析算法:

    通过一些称为”GC_ROOTS”的对象作为起点,开始向下搜索引用它的对象,这个过程走过的路径即”引用链”.当一个对象到”GC_ROOTS”没有任何引用链相连,证明此对象不存活.

    可作为GC_ROORS的对象:

    1. 虚拟机栈(栈帧中的本地变量表)中引用的对象
    2. 方法区中静态属性引用的对象
    3. 方法区中常量引用的对象
    4. 本地方法栈中JNI(Native方法)引用的对象

二.四种引用方式:

  1. 强引用(Strong Reference):

    程序代码当中普遍存在的类似”Object obj = new Object()”这类引用.
    只要强引用存在,该对象永远不会被垃圾收集器回收.

  2. 软引用(Soft Reference):

    SoftReference类实现,用于描述还有用但非必需的对象.
    在系统将要发生内存溢出异常之前,会对这些软引用对象进行二次回收.

  3. 弱引用(Weak Reference):

    WeakReference类实现,也是用于描述非必须对象,强度比软引用更弱.
    无论内存是否足够,弱引用对象都会在下一次垃圾收集发生时进行回收.

  4. 虚引用(Phantom Reference):

    PhantomReference类实现,虚引用无法影响对象的生存时间,唯一用处是在对象被回收时收到一个系统通知.

三.对象死亡的判定过程:

  • 一个对象真正被宣告死亡,至少经过两次标记过程:

    1. 第一次标记:对象经过可达性分析判定不存活

    2. 判断对象是否覆盖或者是否被调用过finalize方法.若未覆盖或已被调用则直接回收对象,否则将对象放入F-Queue队列中去触发finalize方法

    3. 第二次标记: GC对F-Queue中的对象进行筛选,检查对象是否在finalize()中与引用链上的对象进行关联(自救),是则被移出回收集合,否则标记

四.垃圾收集的算法:

  1. 标记 - 清除算法:

    标记阶段:标记出所有需要回收的对象

    清除阶段:统一回收所有被标记的对象

    • 缺点:
      1. 效率:标记和清除两个过程效率都不高.
      2. 空间:标记清除过后会产生大量不连续的内存碎片,导致不发分配足够的连续内存给大对象而提前出发一次垃圾收集
  2. 复制算法(用于新生代):

    将可用内存按容量分为大小相等的两块,每次只使用其中一块.当一块内存用完后,将存活对象复制到另一块上,然后一次性清理使用过的空间.

    • 优点:

      实现简单,运行高效

    • 缺点:

      内存缩小为一半,代价太高

  • 新生代的复制算法:

    将新生代内存分为一个较大的Eden和两个较小的Survivor空间,每次使用Eden和一个Survivor.

    当回收时将Eden和Survivor中存活的对象一次性复制到另一块Survivor中,清理刚用过的Eden和Survivor空间.

    当Survivor中不足以存放上一次存活对象时,这些对象将直接通过分配担保机制进入老年代.

  1. 标记 - 整理算法(用于老年代):

    标记阶段:标记出所有需要回收的对象

    整理阶段:让所有存活的对象都向一端移动,然后直接清理端边界以外的内存

    缺点:

    整理过程耗时

  2. 分代收集算法:

    把Java堆分为新生代和老年代,根据各个年代特点采用合适的收集算法.

    新生代:每次GC都有大批对象死去,只有少量存活,故使用复制算法(实际上经过改进)

    老年代:对象成活率高,使用标记-整理算法

五.内存的分配与回收策略(对象年龄判定):

新生代: Eden + Survivor*2

Eden:Survivor = 8:1

  1. 对象优先在Eden上分配:

    每次分配可用空间为Eden+Survivor,另一个Survivor在下次垃圾收集时用于存放复制的存活对象.

  2. 长期存活的对象进入老年代:

    对象在Survivor区中每存活一次Minor GC,年龄就增加一岁,当年龄增加到一定程度(默认为15岁)就会从新生代晋升到老年代中

  3. 对象年龄的动态判定:

    如果在Survivor空间中相同年龄所有对象所占空间大于Survivor空间的一半,则大于等于此年龄的对象可以直接晋升老年代.

  • 空间分配担保:

    老年代剩余空间 > 之前转入老年代的对象的平均大小 —> 直接Major GC

    老年代剩余空间 < 之前转入老年代的对象的平均大小 && 允许担保失败 —> 直接Minor GC,不需要做Full GC

    老年代剩余空间 < 之前转入老年代的对象的平均大小 && 不允许担保失败 —> 触发Full GC