mono-memory-manager-02

Sgen (分代管理)内存

上一文章写到了mono2.1以前使用的是BOEHM(贝母),mono之后用到的是Sgen(当然jvm也是用的是这一个),为什么mono会换成Sgen呢,按照道理BOEHM其实已经不错了,也不会像引用计数这样,内存泄漏。

什么是Sgen

Sgen分代内存管理,分代内存管理一般分两代,年轻代老年代,年轻代,顾名思义就是刚刚分配的内存空间或者分配不久的内存空间,老年代就是内存空间分配时间比较长。一般来讲普通GC会直接根据年轻代的内存进行Mark&Sweep(标记清除),老年代需要fullGC才会去检查清除操作,这样就可以平时每次清除内存只需要遍历年轻代内存,减少很多遍历次数

Sgen算法

Sgen的年轻代一般会分成3块,一块是分配区(Eden),一块是使用区(Use),一块是空白区(Empty),为什么这样设计呢?根据sun公司统计,平时我们的内存分配到GC阶段,一般之前开辟的空间90%的内存是会直接回收的,而剩余的10%则不会。所以平时开辟的内存都会在分配区,使用区的话是上一次GC阶段留下的还在使用的内存。当分配区满的时候就会触发一次GC操作,此次操作则会把分配区和使用区还在使用的内存,copy进去空白区并且存活计数+1,然后原来的分配区和使用区会直接清空,空白区则变成使用区,原来的使用区就变成空白区,当存活计数为15(jvm操作,不同实现这个数字太一样)则会把这块内存直接存放去老年区,或者现在没有空位了,也会放去老年区

。当老年区也满了,则会触发一次fullgc操作。

这样做的好处有以下几点:

​ 1 每次分配区满了,则只需要尝试把分配区和使用区的内存进行标记清除就好,无需遍历整个内存区(减少遍历次数)

​ 2 copy的时候可以更加好的做内存对齐,内存对齐主要是CPU查找内存命中率会更高,BOEHM很难做到内存对齐,所以每次开辟内存时总会开辟一大块的内存,以达到连续的作用

​ 3 清空内存时,可以把整个区域统一清除,增加清除速度

老年代GC流程则需要经过内存对齐移动,然后清空尾部即可完成

操作图示:

粉红色证明是标记还在使用,灰色或白色(未使用)

开始触发GC时内存分布状态可能是:

eden区

use

标记过程可能会是这种状态:

触发了GC回收操作后empty会变成这样:

老年代回收前可能会是这样

标记过程可能是:

标记移动过程可能是:

清除尾部后状态可能是:

写得比较粗糙,具体实现可以看看参考

写在后面

GC的实现其实并不是那么简单,这里只是讲解了以下比较简单的理念,真正操作起来其实很麻烦,例如eden跟use区触发了GC时,如何保证区域没有被老年区cache住,老年区的大小和年轻区的大小如何分配(jvm好像时8:1),还有其他种种的情况,更深入的时候再写这部分(下一次则放代码)

参考

https://zhuanlan.zhihu.com/p/28250544

打赏

扫一扫,分享到微信

微信分享二维码

请我喝杯咖啡吧~

支付宝
微信