-
引用计数法:
给对象添加一个引用计数器,每一个地方引用它时计数器加1;引用失效时计数器减1.为值为0的对象不可能再被使用.
缺点:
难以解决对象之间循环引用的问题(对象A引用对象B,对象B引用对象A,两个对象无实际意义) -
可达性分析算法:
通过一些称为"GC_ROOTS"的对象作为起点,开始向下搜索引用它的对象,这个过程走过的路径即"引用链".当一个对象到"GC_ROOTS"没有任何引用链相连,证明此对象不存活.
可作为GC_ROORS的对象:
- 虚拟机栈(栈帧中的本地变量表)中引用的对象
- 方法区中静态属性引用的对象
- 方法区中常量引用的对象
- 本地方法栈中JNI(Native方法)引用的对象
- 虚拟机栈(栈帧中的本地变量表)中引用的对象
-
强引用(Strong Reference):
程序代码当中普遍存在的类似"Object obj = new Object()"这类引用. 只要强引用存在,该对象永远不会被垃圾收集器回收.
-
软引用(Soft Reference):
SoftReference类实现,用于描述还有用但非必需的对象. 在系统将要发生内存溢出异常之前,会对这些软引用对象进行二次回收.
-
弱引用(Weak Reference):
WeakReference类实现,也是用于描述非必须对象,强度比软引用更弱. 无论内存是否足够,弱引用对象都会在下一次垃圾收集发生时进行回收.
-
虚引用(Phantom Reference):
PhantomReference类实现,虚引用无法影响对象的生存时间,唯一用处是在对象被回收时收到一个系统通知.
-
一个对象真正被宣告死亡,至少经过两次标记过程:
-
第一次标记:对象经过可达性分析判定不存活
-
判断对象是否覆盖或者是否被调用过finalize方法.若未覆盖或已被调用则直接回收对象,否则将对象放入
F-Queue
队列中去触发finalize方法 -
第二次标记: GC对
F-Queue
中的对象进行筛选,检查对象是否在finalize()中与引用链上的对象进行关联(自救),是则被移出回收集合,否则标记
-
-
标记 - 清除算法:
标记阶段:标记出所有需要回收的对象
清除阶段:统一回收所有被标记的对象
- 缺点:
- 效率:标记和清除两个过程效率都不高.
- 空间:标记清除过后会产生大量不连续的内存碎片,导致不发分配足够的连续内存给大对象而提前出发一次垃圾收集
- 缺点:
-
复制算法(用于新生代):
将可用内存按容量分为大小相等的两块,每次只使用其中一块.当一块内存用完后,将存活对象复制到另一块上,然后一次性清理使用过的空间.
-
优点:
实现简单,运行高效 -
缺点:
内存缩小为一半,代价太高
-
-
新生代的复制算法:
将新生代内存分为一个较大的Eden和两个较小的Survivor空间,每次使用Eden和一个Survivor.
当回收时将Eden和Survivor中存活的对象一次性复制到另一块Survivor中,清理刚用过的Eden和Survivor空间.
当Survivor中不足以存放上一次存活对象时,这些对象将直接通过分配担保机制
进入老年代.
-
标记 - 整理算法(用于老年代):
标记阶段:标记出所有需要回收的对象
整理阶段:让所有存活的对象都向一端移动,然后直接清理端边界以外的内存
缺点:
整理过程耗时 -
分代收集算法:
把Java堆分为新生代和老年代,根据各个年代特点采用合适的收集算法.
新生代:每次GC都有大批对象死去,只有少量存活,故使用复制算法(实际上经过改进)
老年代:对象成活率高,使用标记-整理算法
新生代: Eden + Survivor*2
Eden:Survivor = 8:1
-
对象优先在Eden上分配:
每次分配可用空间为Eden+Survivor,另一个Survivor在下次垃圾收集时用于存放复制的存活对象.
-
长期存活的对象进入老年代:
对象在Survivor区中每存活一次Minor GC,年龄就增加一岁,当年龄增加到一定程度(默认为15岁)就会从新生代晋升到老年代中
-
对象年龄的动态判定:
如果在Survivor空间中相同年龄所有对象所占空间大于Survivor空间的一半,则大于等于此年龄的对象可以直接晋升老年代.
-
空间分配担保:
老年代剩余空间 > 之前转入老年代的对象的平均大小 ---> 直接Major GC
老年代剩余空间 < 之前转入老年代的对象的平均大小 && 允许担保失败 ---> 直接Minor GC,不需要做Full GC
老年代剩余空间 < 之前转入老年代的对象的平均大小 && 不允许担保失败 ---> 触发Full GC