您的位置:首页 > 编程语言 > Java开发

Java 垃圾清理基础理论

2016-05-05 09:56 344 查看

Java 垃圾清理基础理论

标记-清除算法

基本原理

先标记需要回收的对象,然后回收被标记的对象。

标记过程

标记过程(对象的死亡至少要经历两次标记):

首先,如果发现对象从GC root出发不可达,那么就会被第一次标记并进行筛选。筛选条件是此对象是否有必要执行finalize()方法。当对象没有覆盖finalize()方法,或者finalize()方法已经被执行过了,则会被判定为“没有必要执行”。

在确定对象有必要执行finalize()方法后,它将会被放入一个队列中,并由一个较低级别的线程去执行此方法。稍后GC会对此队列进行第二次标记,如果发现依旧从GC roots不可达,那么此对象基本上是要被真的回收了。

也就是说,finalize()方法是对象逃脱死亡的最后一根稻草。如果对象可以在此方法中将自己建立起与GC roots 的可达性,也就是有其他人引用了此对象,那么就可被免于清理

注意:finalize只会被执行一次!

评价

最基础的算法,之后的算发都是依照此法改编而来

不足之处

效率不高:标记和清理的过程效率都不高

空间问题:标记清除之后会产生内存大量碎片,导致后续分配较大对象时,无法找到连续内存的问题而不得不提前触发另一次垃圾收集动作

复制算法

基本原理

为了解决效率问题,此算法诞生了。将可用内存划分为大小相等的两块,每次只使用其中的一块。一块内存用完了,就将存活者复制到另一半内存,然后对已使用过的一半整体回收。这样每次都是对整个半区进行内存回收,内存分配也就不用考虑碎片的情况。

具体应用与实现

现在的商业JVM都采用此算法来回收新生代。根据IBM的研究,98%的对象都是属于短寿命对象。因此无需1:1的分配内存,这样浪费太多。而是将内存分为一个Eden和两个Survivor这三块区域,每次使用Eden和其中一个Survivor区(此Survivor也称为From区)。当GC时,将Eden和Survivor中存活者复制到另一个没有使用的Survivor(此Survivor也被称为To区),然后整体回收刚刚用过的两个区域。

HotSpot默认将需要使用的Eden和Survivor区设置为8:1,占整个新生代内存区的90%。剩下的那个10%作为待用的另一个Survivor。这样将复制算法中原本需要浪费一半的空间缩减为10%。当然,这样的分配比例只是在一般场景下总结出来的,不是说一定就有不多于10%的对象存活(也就是说这个10%的Survivor未必够用)。因此需要依赖其他内存进行分配担保:即在To区不够用时,将装不下的存活对象分配到其他区域。

评价

简单高效,但是代价较高。此算法会将内存缩小到原来的一半。此外,在对象存活率较高时就要进行比较多的复制操作,效率会下降。

标记整理算法

针对老年代下复制算法需要频繁复制的缺陷而设计。此算法分标记和清理两个过程。标记过程同“标记清除”算法。而后续步骤并非直接清理,而是将所有存活对象直接向一端移动,然后清理掉另一端的内存。

分代回收算法

当前商业JVM普遍采用的算法。将Java堆分为新生代和老年代,根据两者生命周期长度不动选用不同算法。新生代采用复制算法,老年代则采用标记清理算法。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签:  java GC 垃圾清理