JAVA虚拟机-GC介绍和垃圾算法理解(二)

1.GC介绍

垃圾回收器(Garbage Collection,GC),顾名思义,垃圾回收就是释放垃圾占用的空间, Java中,程序员不需要去关心内存动态分配和垃圾回收的问题,这一切都交给了JVM来处理。

我们需要考虑一下JVM处理垃圾回收三个问题:

1).哪些内存需要回收?

2).GC什么时候开始回收?

3).如何回收

 

2.垃圾收集方式

2.1 引用计数

2.2 对象遍历引用

 

3.垃圾收集算法

垃圾收集算法主要有:  Mark-sweepmark-compactcopying 三种.
1).复制copying:
找到活动对象拷贝到新的空间
适合存活对象较少情况,增加 内存成本高 (使用于年轻代回收
2).标记清除
从跟开始将活动对象标记,然后再扫描未标记的一次回收
不需要移动对象,仅对不存活对象处理,适合存活对象较多情况,会造成 内存碎片(适合老年代回收)

3).标记压缩

在标记清除基础上,往左移动存活对象

成本高,好处是没有碎片

 

4.三种基本算法简要对比: 

 



时间开销: 
mark-sweep:mark阶段与活对象的数量成正比, sweep阶段与整堆大小成正比 
mark-compact:mark阶段与活对象的数量成正比, compact阶段与活对象的大小成正比 
copying:与活对象大小成正比 

如果把mark、sweep、compact、copying这几种动作的耗时放在一起看,大致关系: 
compaction > copying > marking > sweeping 
还有 marking + sweeping > copying  
 
虽然 compactiont与copying都涉及移动对象,但算法实现存在不同,
compact可能要先计算一次对象的目标地址,然后修正指针,然后再移动对象;
copying则可以把这几件事情合为一体来做,所以可以快一些。 

年轻代:
在分代式垃圾中,年轻代中的对象在minor GC时的存活率应该很低,这样用copying算法就是最合算的,因为其时间开销与活对象的大小成正比,如果没多少活对象,它就非常快;而且young gen本身应该比较小,就算需要2倍空间也只会浪费不太多的空间.那么影响新生代GC的主要因素排序:存活对象数 > 新生代大小 > 老年代算法
 
老年代:
分代式GC里,老年代CMS常用mark-sweep或者是mark-sweep + mark-compact的混合方式,一般情况是用mark-sweep,统计估算碎片量达到一定程度时用mark-compact。老年代被GC时对象存活率可能会很高,而且假定可用剩余空间不太多,这样copying算法就不太合适,于是有另两种算法:mark-sweep,mark-compact.
HotSpot VM中老年代除了CMS之外的其它收集器都是会移动对象的,也就是要么是copying、要么是mark-compact的变种。 
 
CMS为什么用mark-sweep基本算法将其并发化,而不使用移动对象的算法?(ps:原理出自淘宝沙迦) 
 
主要原因分析:GC之外的代码(应用代码)和 GC的代码(collector),两者之间需要保持同步,这样才可以保证两者所观察到的对象图是一致的。 
如果是一个串行、不并发、不分代、不增量式的collector,那么它在工作的时候总是能观察到整个对象图。因而它跟应用代码之间的同步方式非常简单:应用代码一侧不用做任何特殊的事情,只要在需要GC时同步调用collector即可。 

如果有一个分代式的,或者增量式的collector,那它在工作的时候就只会观察到整个对象图的一部分;它观察不到的部分就有可能与应用代码产生不一致,于是需要应用代码配合:它与应用代码之间需要额外的同步。应用代码在改变对象图中的引用关系时必须执行一些额外代码,让collector记录下这些变化。有两种做法,一种是 write barrier,一种是 read barrier。 

通常一个程序里对引用的读远比对引用的写要更频繁,所以通常认为read barrier的开销远大于write barrier,所以很少有GC使用read barrier。 
如果只用write barrier,那么“移动对象”这个动作就必须要完全暂停应用代码,让collector把对象都移动好,然后把指针都修正好,接下来才可以恢复应用代码的执行。也就是说collector“移动对象”这个动作无法与应用代码并发进行。 

如果用read barrier,那移动对象就可以单个单个的进行,而且不需要立即修正所有的指针,所以可以看作整个过程collector都与应用代码是并发的。 

CMS 没有使用read barrier,只用了write barrier。这样,如果它要选用mark-compact为基本算法的话,就只有mark阶段可以并发执行(其中root scanning阶段仍然需要暂停应用代码,这是initial marking;后面的concurrent marking才可以跟应用代码并发执行),然后整个compact阶段都要暂停应用代码。回想最初提到的:compact阶段的时间开销与活对象的大小成正比,这对年老代来说就不划算了。 
于是选用mark-sweep为基本算法就是很合理的选择:mark与sweep阶段都可以与应用代码并发执行。Sweep阶段由于不移动对象所以不用修正指针,所以不用暂停应用代码。 

那 碎片堆积起来了怎么办呢?HotSpot VM里CMS只负责并发收集年老代(而不是整个GC堆)。如果并发收集所回收到的空间赶不上分配的需求,就会回退到使用serial GC的mark-compact算法做full GC。也就是mark-sweep为主,mark-compact为备份的经典配置。但这种配置方式也埋下了隐患:使用CMS时必须非常小心的调优,尽量 推迟由碎片化引致的full GC的发生。一旦发生full GC,暂停时间可能又会很长,这样原本为低延迟而选择CMS的优势就没了。 

新的Garbage-First(G1)GC就是以copying为基础的算法上,把整个GC堆划分为许多小区域(regions),通过每次GC只选择收集很少量region来控制移动对象带来的暂停时间。这样既能实现低延迟也不会受碎片化的影响。 (注意:G1虽然有concurrent global marking,但是可选的,真正带来暂停时间的工作仍然是基于copying算法)

因此在HotSpot VM 只要涉及到对象的移动(copying,compact),就会暂定应用代码(stop-the-world).

 

相关文章:

 

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

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值