基础巩固之Java虚拟机垃圾回收算法

1. 垃圾回收器如何确定对象死亡

1.1 引用计数法

实现原理:给对象中添加一个引用计数器,每当有引用它时,计数器就加1,如果引用失效,计数器值就减1,计数器为0的对象就是没有被再使用过的对象。

优点:引用计数算法实现起来较为简单,判定效率也很高。
缺点:无法解决互相循环引用的问题。

1.2 可达性分析算法

实现原理:通过一系列的称为GC Roots的对象作为起始点,当一个对象到GC Roots没有任何引用链(Reference Chain)相连时,则表明此对象不可用

注意:在Java语言中,可作为GC Roots的对象包括下面几种:

虚拟机栈(虚拟机栈中的本地变量表)中引用的对象。
方法区中静态类属性引用的对象
方法区中常量引用的对象
本地方法栈中native方法引用的对象

注意:在可达性分析算法中,并不是不可达的对象一定会被回收,至少要经历两次标记过程:

如果某个对象在进行可达性分析算法之后没有发现和GC Roots的引用链,它会被标记一次并且进行筛选,筛选的条件就是该对象是否有必要执行finalize()方法,当对象没有覆盖finalize()方法,或者finalize()方法已经被虚拟机调用过,虚拟机在这两种情况下都会认为没必要执行

如果一个对象被判定为有必要执行finalize()方法,那么这个对象会被放置在F-Queue队列中,在稍后会由虚拟机建立一个,低优先级的Finalizer线程去执行它,但不会等它结束

最后一次挣扎

finalize()方法是对象逃脱死亡的最后一次机会,稍后GC将会对F-Queue中的对象进行第二次小规模的标记,如果对象要在finalize()方法中进行自救,只需要重新与引用链上任何一个对象建立关联即可。

2. 垃圾回收算法

2.1 标记-清除算法(Mark-Sweep)

顾名思义,分为标记清除两个阶段:标记出所有需要回收的对象,然后统一回收所有被标记的对象。


缺点:标记和清除的效率都不高,标记清除后会有大量不连续的内存碎片,而过多的内存碎片,会导致后续分配大对象时无法找到足够的连续内存空间,继而触发另一次垃圾回收动作

2.2 复制算法(Copying)

将可用内存分为大小相同两块。每次只用一块,当一块空间用完了,就将还存活的对象复制到另一块上,然后将刚使用过的内存空间一次清理掉。这样使得每次都是对其中的一块进行内存回收,内存分配时也就不用考虑内存碎片等复杂情况。实现简单,运行高效。

优点:算法实现简单,内存效率高,不易产生碎片。
缺点:可用内存被压缩到了原本的一半。且存活对象增多的话,Copying算法的效率会大大降低。

2.3 标记-整理(Mark-Compact)

复制收集算法在对象存活率高的时候就要执行较多的复制操作,效率将会变低。更关键的是,如果不想浪费50%的空间,就需要有额外的空间进行分配担保用于应付半区内存中所有对象都100%存活的极端情况,所以在老年代一般不能直接选用这种算法。

因此人们提出另外一种“标记-整理”(Mark-Compact)算法由于老年代中的对象生存周期都较长,有人提出“标记-整理”算法,标记过程和“标记-清理”一样,但在清除已死对象的同时会对存活对象进行整理,这样可以减少碎片空间。

2.4 分代收集算法

当前商业虚拟机的垃圾收集都是采用分代收集(Generational Collecting)算法,这种算法并没有什么新的思想出现,只是根据对象不同的存活周期将内存划分为几块。一般是把Java堆分作``新生代和老年代,这样就可以根据各个年代的特点采用最适当的收集算法。在新生代中,每次垃圾收集时都发现有大批对象死去,只有少量存活,那就用复制算法,只要少量复制成本就可以完成收集。而老年代中因为对象的存活率较高、周期长,就用标记-整理标记-清除算法来回收。

新生代复制回收机制

将新生代区域分为EdenFrom SurvivorTo Survivor,比例为8:1:1

实现原理
a. Eden区域最大,对外提供堆内存,当Eden区快满的时候,进行Minor GC,把存活对象放入From Survivor区域,然后清空Eden区域,移动之后会标记相应的age为1,这样就 完成了一次Minor GC
b. 由于Eden区域被清空,对外提供堆内存,当Eden区域又快满的时候,就会再次触发Minor GC,将Eden区域和From Survivor区域中存活的对象复制到To Survivor区域中,然后将Eden以及From Survivor区域清空,标记从Eden复制到To Survivor区域中的对象年龄设置为1,再将从From Survivor区域中复制过去的对象年龄+1,这样完成了第二次GC
c. 同上,当Eden区域再次快满的时候,就会将Eden区域以及To Survivor区域中的存活对象,复制到From Survivor区域中,标记从Eden复制到From Survivor区域中的对象年龄设置为1,再将从To Survivor区域中复制过去的对象年龄+1,这样完成了第三次GC
d. …后面的步骤跟之前的b、c步骤交替进行
e. 当一个存活对象的年龄熬过一定次数之后(默认为15次),该对象就会被移动到老年代。

老年代-标记整理回收机制

老年代一般存放的是存活时间较久的对象,所以每一次 GC 时,存活对象比较较大,也就是说每次只有少部分对象被回收。
因此,根据不同回收机制的特点,这里选择 存活对象多,垃圾少 的标记整理 回收机制,仅仅通过少量地移动对象就能清理垃圾,而且不存在内存碎片化。

3 垃圾收集器

如果说垃圾收集算法是内存回收的方法论,那么垃圾收集器就是内存回收的具体实现。

3.1 Serial收集器

缺点:它在进行垃圾回收的时候,必须暂停所有的工作线程。

3.2 ParNew收集器

它是Serial收集器的多线程版本,用于新生代内存回收,可以配合CMS使用

3.3 Parallel Scavenge收集器

它是一个新生代收集器,也是使用复制算法的收集器,它的目标是达到一个可控制的吞吐量。

3.4 Serial Old收集器

他是Serial收集器的老年代版本,使用的是标记整理算法。

3.5 Parallel Old收集器

他是Parallel Scavenge收集器老年代版本,使用的是标记整理和多线程。

3.6 CMS收集器

它是一种以获取最短停顿时间为目标的收集器。基于标记-清除算法实现的,整个过程如下:
初始标记(CMS initial mark)
并发标记(CMS concurent mark)
重新标记(CMS remark)
并发清除(CMS initial sweep)
其中初始标记重新标记依然需要暂停工作线程。

优点:并发手机,停顿低。

参考:
《深入理解Java虚拟机》

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值