Java垃圾收集器与内存分配策略

本文是对《深入理解Java虚拟机》,第三章 垃圾收集器与内存分配策略 的总结。

对象死了吗?

引用计数法

给对象添加一个引用计数器,每当有一个地方引用它时,计数器加一,引用失效时,计数器减一,任何时刻计数器为0的对象就是不可能再被使用的。 实现简单,判定效率高,但是很难解决对象间循环引用的问题

可达性分析法

通过一系列的称为“GC Roots”的对象作为起始点,从这些节点开始向下搜索,搜索所走过的路径称为引用链(Reference Chain),当一个对象到GC Roots没有任何引用链相连(用图论的话来说,就是从GC Roots到这个对象不可达)时,则证明此对象是不可用的。

引用

强引用 new,软引用 SoftReference, 弱引用 WeakReference,虚引用 PhantomReference

如果宣告对象死亡,至少要经历两次标记过程:如果对象在进行可达性分析后发现没有与GC Root相连接的引用链,那它将会第一次标记并且进行一次筛选,筛选的条件是此对象是否有必要执行finalize()方法。 如果这个对象被判定为有必要执行finalize()方法,那么这个对象会放置在一个叫做F-Queue的队列之中,并在稍后由一个虚拟机自动创建的、低优先级的Finalizer线程去执行它。

回收方法区

满足以下3个条件:
1.该类所有的实例都已经被回收,也就是Java堆中不存在该类的任何实例。
2.加载该类的ClassLoader已经被回收。
3.该类对应的java.lang.Class对象没有在任何地方被引用,无法在任何地方通过反射访问该类的方法。

垃圾收集算法

复制算法
标记-整理算法
标记-清除算法

根据对象存活周期的不同将内存划分为几块。一般是把Java堆分为新生代和老年代
新生代:每次垃圾收集时有大批对象死亡,少量存活, 选用 复制算法
老年代:对象存活率高、没有额外空间对它进行分配担保,使用 标记-整理 或者 标记-清理

垃圾收集器

垃圾收集器是内存回收的具体实现。
到目前为止没有一个万能的收集器,所以选择的是对具体应用最合适的收集器。

Serial收集器

单线程
在进行垃圾收集时,必须暂停其他所有的工作线程,直到它收集结束

ParNew收集器

Serial收集器的多线程版本

CMS收集器

最短回收停顿时间
并发收集、低停顿
标记-清除
步骤:初始标记、并发标记、重新标记、并发清除

G1收集器

特点:并行与并发、分代收集、空间整合(不会产生内存空间碎片)、可预测的停顿
步骤:初始标记、并发标记、最终标记、筛选回收

注:
并行(Parallel):指多条垃圾收集线程并行工作,但此时用户线程仍然处于等待状态
并发(Concurrent):指用户线程与垃圾收集线程同时执行(但不一定是并行的,可能会交替进行),用户程序在继续运行,而垃圾收集程序运行于另一个CPU上

内存分配与回收策略

对象优先在Eden分配

大对象直接进入老年代

长期存活的对象将进入老年代

如果对象在Eden出生并经过第一次Minor GC后仍然存活,并且能被Survivor容纳,将被移动到Survivor空间中,并且对象年龄设为1,。对象在Survivor区中每“熬过”一次Minor GC,年龄就增加1岁,当它的年龄增加到一定程度(默认为15岁),就将会被晋升到老年代中。

动态对象年龄判定

如果在Survivor空间中相同年龄所有对象大小的总和大于Survivor空间的一半,年龄大于或等于该年龄的对象就可以直接进入老年代