在《Jvm垃圾回收器(基础篇)》中咱们主要学习了判断对象是否存活仍是死亡?两种基础的垃圾回收算法:引用计数法、可达性分析算法。以及Java引用的4种分类:强引用、软引用、弱引用、虚引用。和方法区的回收介绍。html
那么接下来咱们重点研究下虚拟机的几种常见的垃圾回收算法:标记-清除算法、复制算法、标记-整理算法、分代收集算法。java
最基础的收集算法,总共分为‘ 标记 ’和‘ 清除 ’两个阶段算法
标记出全部须要回收的对象布局
在《Jvm垃圾回收器(基础篇)》中说明了判断对象是否回收须要两次标记,如今咱们再来回顾一下post
一次标记:在通过可达性分析算法后,对象没有与GC Root相关的引用链,那么则被第一次标记。而且进行一次筛选:当对象有必要执行finalize()方法时,则把该对象放入F-Queue队列中。学习
二次标记:对F-Queue队列中的对象进行二次标记。在执行finalize()方法时,若是对象从新与GC Root引用链上的任意对象创建了关联,则把他移除出“ 即将回收 ”集合。不然就等着被回收吧!!!url
对被第一次标记切被第二次标记的,就能够断定位可回收对象了。spa
两次标记后,还在“ 即将回收 ”集合的对象进行回收。.net
执行过程以下:代理
优势:基础最基础的可达性算法,后续的收集算法都是基于这种思想实现的
缺点:标记和清除效率不高,产生大量不连续的内存碎片,致使建立大对象时找不到连续的空间,不得不提早触发另外一次的垃圾回收。
将可用内存按容量分为大小相等的两块,每次只使用其中一块,当这一块的内存用完了,就将还存活的对象复制到另一块内存上,而后再把已使用过的内存空间一次清理掉。
复制算法执行过程以下:
优势:实现简单,效率高。解决了标记-清除算法致使的内存碎片问题。
缺点:代价太大,将内存缩小了一半。效率随对象的存活率升高而下降。
如今的商业虚拟机都采用这种算法(须要改良1:1的缺点)来回收新生代。
分代垃圾收集基于弱代理论。具体描述以下:
其中IBM研究代表:新生代中98%的对象都是"朝生夕死"; 因此并不须要按1:1比例来划份内存(解决了缺点1);
新生代内存分配一块较大的Eden空间和两块较小的Survivor空间
每次使用Eden和其中一块Survivor空间
回收时将Eden和Survivor空间中存活的对象一次性复制到另外一块Survivor空间上
最后清理掉Eden和使用过的Survivor空间。
Hotspot虚拟机默认Eden和Survivor的大小比例是8:1。
若是另外一块Survivor空间没有足够内存来存放上一次新生代收集下来的存活对象,那么这些对象则直接经过担保机制进入老年代。
关于分配担保的内容,我会在讲述垃圾收集器时详细描述。
标记-整理算法是根据老年代的特色应运而生。
标记过程和标记-清理算法一致(也是基于可达性分析算法)。
和标记-清理不一样的是,该算法不是针对可回收对象进行清理,而是根据存活对象进行整理。让存活对象都向一端移动,而后直接清理掉边界之外的内存。
标记-整理算法示意图
优势:不会像复制算法那样随着存活对象的升高而下降效率,不像标记-清除算法那样产生不连续的内存碎片
缺点:效率问题,除了像标记-清除算法的标记过程外,还多了一步整理过程,效率更低。
当前商业虚拟机的垃圾收集都是采用“ 分代收集 ”算法。
根据对象存活周期的不一样将内存划分为几块。通常把java堆分为新生代和老年代。JVM根据各个年代的特色采用不一样的收集算法。
新生代中,每次进行垃圾回收都会发现大量对象死去,只有少许存活,所以比较适合复制算法。只须要付出少许存活对象的复制成本就能够完成收集。
老年代中,由于对象存活率较高,没有额外的空间进行分配担保,因此适合标记-清理、标记-整理算法来进行回收。
----对《深刻理解Java虚拟机》第3章垃圾收集器与内存分配策略 3.3小节总结。接下来总结3.5垃圾收集器。
参考:
《深刻理解Java虚拟机》
https://blog.csdn.net/tjiyu/article/details/53983064