GC算法与种类
总阅读次
GC算法与种类
本文将介绍 GC的概念、GC算法、可触及性概念、Stop-The-World现象
运行环境
|
|
GC的概念
GC是Garbage Collection 垃圾收集。
在Java中GC的操作是在堆和永久区进行的
GC算法
引用计数法
引用计数法,老牌垃圾回收算法,通过计算引用者的数量来回收垃圾。
使用者,python、ActionScript3
引用计数法的问题
- 引用和去引用伴随着加法和减法,影响性能
- 很难处理循环引用
2标记的这个对象,被根对象引用。当根对象引用消失了。这个时候按照引用计数法的规则,该标记的引用对象还是有一个引用对象。导致该无效的对象无法回收。
标记-清除法
标记清除算法是很多垃圾收集算法的思想基础。标记-清除算法将垃圾回收分为两个阶段:标记阶段和清除阶段。一种可行的实现是,在标记阶段,首先通过根节点,标记所有从根节点开始可达的对象。因此,未被标记的对象就是未被引用的垃圾对象。清除阶段,清除所有未被标记的对象。
通过图例表示:
标记-压缩法
标记-压缩法适合用于存活对象较多的场合,如老年代。它在标记-清除算法上做了一些优化,和标记-清除算法一样,标记-压缩算法也是从根节点开始标记存活对象;但之后并不是简单的清除未被标记的垃圾对象,而是将所有的存活对象压缩到内存的一端。之后,清除边界外所有的空间
通过图例表示:
上图没有表现标记阶段,上图第一部分表示了标记出存活对象后的移动到内存的边界,第二部分说明了清除边界外的所有空间。
相对于 标记-清除法 的优势,清除后的空间都是连续的内存空间,但是增加了内存的移动成本
复制算法
将内存分成大小相同的两块,每次只使用其中一块,在垃圾回收时,将正在使用的内存中的存活对象复制到未使用的内存中,之后,清除正在使用的内存块中所有的对象,交换两个内存块的角色,完成垃圾回收。
与标记-清除算法相比,复制算法是一种比较高效的回收算法;不适合对象较多的场合,如老年代
通过图例表示:
通过上面的描述,可以看出复制算法的空间是浪费了一半,因为每次只能使用其中的一半。
上图描述了堆中对象的晋升规则:eden区比较大的对象直接晋升到老年代,因为from和to两个区都比较小(比较小的原因是:使用了复制算法,只能使用一半的存储空间,越大浪费的越多),必须有内存进行担保,这里就是老年代进行担保。
eden区比较小的对象晋升到from或to区。
form或to区的对象经过多次(默认15次)GC还是存活的话,会晋升到老年代
其他对象就会清空
下图通过GC日志分析复制算法:
|
|
新生代代的总大小:(0x00000000f9350000-0x00000000f8600000)/1024/1024 = 13m,但是上面显示年轻代总共大小是12m,少了幸存区的一半
分代思想
依照对象的存活周期进行分类,短生命周期的对象归为新生代,长生命周期的对象归为老年代。
根据不同的代的特点,选取合适的收集算法:
- 少量对象存活,适合复制算法
- 大量对象存活,适合标记清理或者标记压缩
GC算法总结
- 引用计数
- 没有被Java采用
- 标记-清除
- 老年代使用
- 标记-压缩
- 老年代使用
- 复制算法
- 新生代使用
可触及性
可触及的
- 从根节点可以触及到这个对象
可复活的
- 一旦所有引用被释放,就是可复活状态
- 因为finalize()中可能复活该对象
不可触及的
- 在finalize()后,可能会进入不可触及状态
- 不可触及的对象不可复活
- 可以回收
通过代码进行上述描述的演示
123456789101112131415161718192021222324252627282930313233public class CanReliveObj {private static CanReliveObj obj;protected void finalize() throws Throwable {super.finalize();System.out.println("CanReliveObj finalize method called");obj = this;}public static void main(String[] args) throws InterruptedException{obj = new CanReliveObj();obj = null;System.gc();Thread.sleep(1000);if(obj==null){System.out.println("obj is null");}else {System.out.println("obj is not null");}System.out.println("second gc");obj = null;System.gc();Thread.sleep(100);if(obj==null){System.out.println("obj is null");}else {System.out.println("obj is not null");}}}输出结果如下:
1234CanReliveObj finalize method calledobj is not nullsecond gcobj is null对象通过调用
finalize
方法确实实现了复活,并且只会调用一次。通过上面的代码我们需要知道,关于finalize方法的相关经验:
- 经验:避免使用finalize(),操作不慎可能导致错误。
- 优先级低,何时被调用, 不确定
- 何时发生GC不确定
- 可以使用try-catch-finally来替代它
根节点
有哪些对象可以作为根
- 栈中引用的对象
- 全局对象
- JNI(Java native interface)方法栈中的对象
Stop-The -World
stop the world,Java中一种全局暂停的现象;全局停顿,所有Java代码停止运行,native代码可以运行,但不能和JVM交互;多半由于GC引起
通过下面代码描述 stop the world
|
|
预期,每秒钟有10条输出
在这之中有从3153直接到3504的,中间这些时间就是GC所消耗的时间