上一篇学习了判断Java对象已死的算法分析,而今天学习的算法是对在判断出对象已死的后续操作的算法,是各种垃圾收集器的方法理论。
| 目录
一、 标记-清除算法
二、标记-复制算法(主要用于新生代)
三、标记-整理算法(可用于老年代)
四、分代理论(重点)
五、总结
一、 标记-清除算法
a、算法过程:标记、清除
标记要清除的对象,然后统一清除要回收的对象,或者反过来也行,标记存活的对象,清理未被标记的。
缺点:
1、执行效率不稳定,标记和清除的两个过程会随着对象增长而效率降低
2、内存碎片化,内存太碎会导致分配大对象的时候没有足够的内存而不得不再次触发收集。
这个算法比较简单直接,不过缺陷也比较明显,不过这是最基础的算法,后面两个算法就是基于“标记-清除”的优化版本而已。
二、标记-复制算法(主要用于新生代)
a、算法过程
将内存分成相同的两份,每次只用其中一份,当一份用完的时候触发一次回收,把存活的对象复制到另外一边,然后原来那一半清理。
优点:这样做不用担心内存碎片,而且只要把存活的对象一个一个顺序移到另外一边就是了,实现简单、运行高效。
缺点:浪费了一半的内存。
优化:根据经验,在新生代对象98%都熬不过第一轮收集,所以我们在新生代的划分就可以不用1:1的比例来划分了。在JVM中新生代的区域划分:一个80%的Eden、两个各占10%的Survivor。
三、标记-整理算法(可用于老年代)
标记-整理算法的标记过程和"标记-清除算法"一样,而后面是让所有存活的对象往一端移动。
优点:解决了“标记-复制算法”在对象存活较高时会进行较多的复制操作,效率会降低问题,解决了“标记-清除算法”存在内存碎片化问题。
缺点:标记-整理要移动对象并更新所有引用,所有效率也会低。
移动则内存回收时会更复杂(要更新引用),不移动则分配时更复杂(存在内存碎片)。
四、分代理论(重点)
大家都知道我们的Java堆中分新生代、老年代,那么他们从何而来,又为什么要这么分呢?
首先是有两个分代理论假说:
1、弱分带假说:绝大多数对象都是朝生夕灭;
2、强分带假说:熬过越多次垃圾收集过程的对象就是越难以消亡;
这两个假说奠定了堆的一致设计原则:收集器应该将Java堆划分出不同的区域,然后将回收的对象依据其年龄(年龄就是熬过垃圾收集过程的次数)分配到不同的区域之中存储。
像这样相当于把对象按回收的难易程度区分,就可以在不同的区域实行不同的算法,比如在易回收的区域(新生代)我们只用关注存活下来的对象(占极少数),其他对象直接回收。而在不易回收的区域(老年代)只用关注那些应该被回收的对象(占少数),并且因为回收的数量少,所以应该尽量少回收这个地方。
通过这样就兼顾了垃圾收集的时间开销和内存空间有效利用。
因此不同的区域就对应有了不同的回收类型:"Minor GC"、“Major GC”、“Full GC”,针对不同的区域安排和存储的对象特征安排不同的垃圾回收算法:“标记-复制算法”、“标记-清除算法”、“标记-整理算法”。
也存在了两个区域:新生代(Young Generation)、老年代(Old Generation)
问题:我们都知道对象一般不是孤立存在的,都是一个对象引用一个对象,一个对象再引用另外一个对象。那么如果分代回收只扫描了新生代区域的对象能判定一个对象死亡吗?这里就引用出来第三个假说。
3、跨代引用假说:跨代引用相对于同代引用来说只占极少数。
讲解:可以想象两个有相互引用的对象,他们一般来说都会同时存活和消亡,他们两个要么一起消亡了,要么就一起步入老年代。假如新生代对象被老年代对象引用,那么由于老年代不容易消亡,那么新生代对象不久也会变成老年代对象。对应新生代引用老年代,新生代的回收基本不会影响引用的老年代对象,因为被引用的老年代肯定还有其他引用,不然他怎么进的老年代。
各种收集的介绍:
部分收集(Partial GC):
新生代收集(Minor GC/Young GC),
老年代收集(Major GC/Old GC),
混合收集(Mixed GC)(G1收集器会有这种行为)
整堆收集(Full GC)
五、总结
三种算法简单对比:
三大分代理论假说:
1、弱分带假说:绝大多数对象都是朝生熄灭
2、强分带假说:熬过越多次垃圾收集过程的对象就是越难以消亡
3、跨代引用假说:跨代引用相对于同代引用来说只占极少数。
两个重点区域:新生代(Young Generation)、老年代(Old Generation)
在JVM中新生代的区域划分:一个80%的Eden、两个各占10%的Survivor
三个重点名称:新生代收集(Minor GC/Young GC),老年代收集(Major GC/Old GC),整堆收集(Full GC)。
思考:
即使是针对回收对象这样一件事,表面上我们只需要找到一种算法去实现即可,可是根据对象特征对对象进行了划分,他虽然带来了一些问题,但是在不同的划分区域采用不同的算法,极大的提高了性能。
这启发了我们在面对一件事的时候,即使整体表面上是一件事,如果可以仔细分析特征,然后划分分类处理,也许可以找到更好的办法。
Java程序员日常学习笔记,如理解有误欢迎各位交流讨论!