第三章:垃圾收集器与内存分配策略
可达性分析算法
-
在Java中就是通过可达性分析来判断对象是否存活的。
-
算法的基本思路:通过一系列称为“GC ROOTS”的对象作起始点,从这些节点开始向下搜索,搜索经过的路径就是引用链。当一个对象到GC Roots 没有任何引用(即从GC Roots到这个对象不可达)时,证明此对象是不可用的。则对象会被判定为可回收对象。
-
可作为GC Roots的对象:
-
虚拟机栈(栈帧中的本地变量表)中引用的对象
-
方法区中类静态属性引用的对象
-
方法区中常量引用的对象
-
本地方法(Native方法)栈中引用的对象
-
Java中引用的类型:
-
强引用:普遍存在的引用,只要强引用还在,垃圾收集器永远不会回收。
-
软引用:非必需,在系统将要发生内存溢出之前,会把这些对象列入回收范围进行二次回收。
-
弱引用:被弱引用关联的对象只能生存到下一次垃圾收集之前。
-
虚引用:为一个对象设置虚引用的唯一目的,就是在这个对象被垃圾收集器回收时收到一个系统通知。
对象的死亡
-
当对象在可达性分析后发现没有与GC Roots相连的引用链,它会被第一次标记,并进行一次筛选。
-
筛选的条件是:此对象是否有必要执行finalize方法
-
如果此对象没有覆盖finalize方法,或finalize方法已被调用过(finalize方法只能被调用一次),则不会执行finalize方法。即对象会在第二次标记的时候被回收。
-
如果对象执行了finalize方法,需要重新与引用链上任何一个对象建立关联。这样在第二次标记时,它会被移出“即将回收”的集合。
垃圾收集算法
-
标记-清除算法:首先标记所有需要回收的对象,在标记完成后统一进行回收。效率不高,而且会产大量内存碎片。
-
复制算法:它将可用的内存分为大小相等的两块,每次只使用其中的一块。在进行垃圾收集的时候,将还存活的对象复制到另一块区域上。然后清理掉这一块所有的已使用空间。只是代价太高,内存变为了原来的一半。
-
虚拟机中新生代的回收就是使用的这种算法。只是虚拟机把内存分为了一块较大的Eden区域和两块较小的Survivor空间(8:1:1)。每次使用Eden区域和其中一块Survivor区域。回收时,将还存活的对象复制到另一块Survivor区域上。如果Survivor空间不够用时,可以使用老年代的内存进行分配担保。
-
-
标记-整理算法:回收时,让所有存活的对象都向一端移动,然后直接清理掉端边界以外的内存。
-
老年代使用的算法。
-
垃圾收集器
-
Serial:单线程收集器,在进行垃圾收集时,会暂停用户的所有工作线程。不过简单而高效,对应单CPU情况效率高。可以与Serial Old和CMS老年代收集器使用。
-
ParNew:Serial的多线程版本。它在单CPU情况下没有Serial好。也是能与Serial Old和CMS一块使用。
-
Parallel Scavenge:它的目标是尽可能高的吞吐量。可以与Serial Old和Parallel Old一块用。
-
CMS:以最短停顿时间为目标的收集器。基于“标记-清除”算法实现。可以与Serial和ParNew组合。
-
Serial Old:Serial收集器的老年代版本。可以与任意新生代收集器组合。
-
Parallel Old:Parallel收集器的老年代版本。只能与Parallel组合。
-
G1:最新的收集器。也是最强大的。它既可以收集新生代,也可以收集老年代。
对象的分配与回收策略
-
大多数情况下,对象在新生代Eden区分配,当Eden中没有足够的空间时,虚拟机将发起一次Minor GC。
-
对于足够大的对象,虚拟机会直接在老年代直接分配。
-
虚拟机给每一个对象都定义了一个对象年龄计数器,对象每经历一次Minor GC年龄增加1岁,当年龄增加到一定程度(默认15岁),就会晋升到老年代。
-
虚拟机并不是永远的要求对象年龄必须达到某个值,如果在Survivor空间中相同年龄所有对象大小的总和大于Survivor空间的一半,年龄大于等于该年龄的对象就可以直接进入老年代。
-
Minor GC:指发生在新生代的垃圾收集动作。比较频繁,速度也快。
-
Major GC:指发生在老年代的垃圾收集动作。一般发生Major GC就伴随着至少一次的Minor GC(在Parallel收集器里,有直接Major GC的动作),速度一般比Minor GC慢10倍。
-
Full GC:针对整个堆的GC。