【Java】垃圾回收算法

一、判断对象存活情况

1、引用计数算法(非Java所使用的方法)

每个对象都维护一个引用计数器,每有一个地方引用这个对象的时候,引用计数器就加1;当引用失效时,引用计数器就减1,当引用计数器的值为0时表示这个对象不可能再被使用了。

2、可达性分析算法

虚拟机维护一系列称为“GC Roots”的对象作为根节点的树,所有引用了这个“GC Root”的对象都是其子节点,由此可以得到以可引用树,当一个对象不能通过任何一个“GC Roots"到达时,该对象就会被判定可回收。

可作为GC Roots的对象:

虚拟机栈(栈帧中的局部变量表)中引用的对象

方法去中类静态属性引用的对象

方法区中常量引用的对象

本地方法栈中JNI(即一般说的Native方法)引用的对象

3、方法区回收

方法区回收的主要内容:废弃常量和无用的类

废弃常量回收方法与Java堆中的对象类似,当一个常量没有任何一个对象引用了该常量时,在下一次GC时就有可能被清理出常量池,常量池中的其他类(接口)、方法、字段符号引用也是如此。

无用的类条件要苛刻很多:

(1)该类的所有实例已经被回收

(2)加载该类的ClassLoader已经被回收

(3)该类对应的java.lang.Class对象没有在任何地方被引用,无法通过反射访问该类的方法

 

二、垃圾收集算法

1、分代收集算法

分代收集就是根据对象的存货周期将内存分为几块,一般分为新生代和老年代,根据不同收集算法又可细分,比如复制算法中新生代又分为Eden、To Survivor、From Survivor三个不同的区域。

 

2、标记-清除算法

(1)标记已死亡的对象(即用上述判断对象是否存活的算法)

(2)统一清除标记对象

存在的问题:

(1)效率问题,标记和清除都需要”Stop the World“,即停下所有用户线程才能进行

(2)会产生大量内存碎片,可能导致分配大对象时不得不再次GC以获取足够的连续内存空间

 

3、复制算法

复制算法的思想是将新生代内存空间分配为一块较大的Eden空间、两块较小的Survivor空间,这就是To Survivor和From Survivor空间的由来,两个Survivor空间大小相等,每次只是用其中一块To Survivor,当这一块内存用完时,就将GC过后还存活的对象复制到另外一块From Survivor上面,然后两块内存的名字互换,使得每次都是To Survivor空间保存对象。

这种算法一般应用在新生代上面,因为新生代占用的内存较老年代来说比较小,且其对象存活率远比老年代要低,因此不需要经常性的进行复制操作。

当GC过后,新生代Survivor空间仍然不足时,就需要向老年代进行分配担保,使得新生对象直接进入老年代。

好处:不会产生内存碎片,实现简单且高效

不足:内存缩小为原来的一半,空间代价太高

 

4、标记-整理算法

(1)标记过程与“标记-整理”算法一致

(2)整理过程是将所有存活的对象都向同一端移动,然后清除掉存货对象边界外的内存就可以了

这种算法一般应用在老年代,因为老年代对象存活率高,使用复制算法会导致频繁的复制操作而导致效率下降。

好处:不用花费额外的空间来进行复制操作;同时也不会产生内存碎片而导致新生大对象创建时需要频繁GC

 

三、安全点和安全区

为了直接得知内存中哪些地方存放着对象引用,Hotspot中维护一组称为OopMap的数据结构来得知,虚拟机会在特定的地方记录对象引用并生成一个OopMap,但不是所有的对象引用都对应一个OopMap,那样的话在GC的时候会浪费太多资源,造成过长的“Stop the World”时间,因此只有特定位置才会生成OopMap。

这些被记录的引用点OopMap点又被称为安全点(SafePoint),当程序到达安全点的时候才能停下来开始GC。

安全区(Safe Region)就是将一维的安全点扩展为二维的安全区域,如果在一段代码片段中引用关系不会改变,那么这个区域中GC都是安全的,因此就可以作为安全区。

一个问题是如何保证所有的线程都能在安全点阻塞,有以下两种方法:

1、抢先式中断(Preemptive Suspension)(少用)

在GC发生时先中断所有线程,然后检查所有线程,如果有线程中断的地方不在安全点上面就恢复该线程,并让该线程跑到最近的安全点。

2、主动式中断(Voluntary Suspension)(常用)

设置一个中断标志,由各个线程执行时轮询这个表示,当发现中断标志为真时就将自己中断挂起,只要保证轮询标志的地方和安全点是重合的就可以了。

 

四、垃圾收集器

在Hotspot虚拟机中,有很多个垃圾收集器在多个场景下发挥各自的作用,每一个垃圾收集器都有自己擅长的领域,如Serial、ParNew专门收集新生代的对象,而CMS等专门收集老年代对象,没有一款虚拟机能够胜任所有情况,但是可以根据具体业务选取合适的垃圾收集器。

上图中有连线的虚拟机可以配合使用,如Serial/Serial Old配合就可以处理整个虚拟机的所有GC工作。

1、Serial收集器

(1)暂停其他所有线程

(2)单线程进行垃圾收集(复制算法)

优点:简单高效;没有线程交互开销,在单CPU中效率最高;Client模式首选

缺点:多核时不能很好地利用CPU

 

2、ParNew收集器(多线程版Serial收集器)

(1)暂停其他所有线程

(2)多线程进行垃圾收集(复制算法)

优点:简单高效;能够很好地利用多CPU多核;Server模式首选

缺点:单核时由于线程开销导致其效率低于Serial收集器

 

3、Parallel Scavenge收集器

(1)暂停其他所有线程

(2)多线程进行垃圾收集(复制算法)

优点:简单高效;能够很好地利用多CPU多核;吞吐量优先;自适应策略调节参数

缺点:单核时由于线程开销导致其效率低于Serial收集器

 

4、Serial Old收集器

(1)暂停其他所有线程

(2)单线程进行垃圾收集(标记-整理算法)

优点:简单高效;单核时效率高;与Parallel Scanvenge收集器配合;作为CMS发生Concurrent Mode Failure时后备方案

缺点:不能利用多核或者多CPU

 

5、Parallel Old收集器

(1)暂停其他所有线程

(2)多线程进行垃圾收集(标记-整理算法)

优点:简单高效;能够很好地利用多CPU多核;吞吐量优先;自适应策略调节参数

缺点:单核时由于线程开销导致其效率低于Serial收集器

 

6、CMS收集器

(1)初始标记(CMS initial mark)

“Stop The World”,标记GC Roots能直接关联到地对象

(2)并发标记(CMS concurrent mark)

Tracing GC Roots,遍历GC Roots引用树

(3)重新标记(CMS remark)

“Stop The World”,修正因用户线程继续执行而产生变动的对象的标记记录

(4)并发清除(CMS concurrent sweep)

优点:需要“Stop The World”的两个阶段其实占用的时间很少,保证很低的停顿时间;理论上整个CMS收集垃圾的过程几乎是并发执行的,因此效率很高

缺点:CMS对CPU资源非常敏感,其默认启动线程数为(总CPU数+3)/4,可能导致用户线程变慢很多;无法处理浮动垃圾,可能出现“Concurrent Mode Failure”而导致另一次Full GC;基于”标记-清除“算法,会产生大量内存碎片

 

7、G1收集器 

G1收集器的思想是“化整为零”,将堆内存分为多个大小相等的独立区域(Region),进而跟踪每个Region中垃圾回收的效果(对于回收空间和回收时间成本的考量)进行优先回收,保证了G1收集器能够以较高的效率收集。

(1)初始标记(Initial marking)

“Stop The World”,标记GC Roots能直接关联到地对象,修改TAMS(Next Top at Mark Start)的值

(2)并发标记(Concurrent marking)

Tracing GC Roots,遍历GC Roots引用树

(3)最终标记(Final marking)

“Stop The World”,修正因用户线程继续执行而产生变动的对象的标记记录,把Remembered Set Logs的数据整合到Remembered Set中

(4)筛选回收(Live Data Counting and Evacuation)

将各个Region的回收价值和成本进行排序,根据用户期望的GC时间来回收

优点:并行与并发;分代收集;空间整合;可预测停顿

缺点:一个对象的引用可能分布于多个Region,很难只看自己的Region内部就可以统计处垃圾堆积的价值,因此引入了Remembered Set来避免全堆扫描,虚拟机在发现程序对Reference类型的数据进行写操作时,会将写操作中断,并检查Reference引用的对象是否处于不同的Region中,是的话就将相关引用信息传到该Reference引用对象所处的Region的Remembered Set中,GC时检查Remembered Set就可避免全堆扫描了

 

五、垃圾收集器参数

 

六、内存分配和回收策略

1、对象优先在Eden区分配

2、大对象直接进入老年代

3、长期存活的对象进入老年代

4、动态对象年龄判断

如果Survivor空间中相同年龄的所有对象大小总和大于Survivor空间的一半,那么年龄大于等于该年龄的对象就可以直接进入老年带

5、空间分配担保

老年代最大可用连续空间必须大于新生代所有对象的总空间,以确保Minor GC成功,否则就有风险失败,此时必须改为Full GC

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值