Erlang并发机制 – 垃圾回收

爱⌒轻易说出口 提交于 2019-12-06 14:05:21

Erlang中每个进程都有独立的堆内存,默认的大小是233个words(可配置),并以Fibonacci序列的顺序增长(233对应fib(11))。不过,当堆内存增大到一定程序时,增长速度减缓,比如内存大于fib(35)=14M的时候,堆内存开始不以Fibonacci序列增长(具体参见[$R15B_OTP_SRC/erts/emulator/beam/erl_gc.c --> erts_init_gc]里的说明)。一般情况下,进程所用到的数据都放在各自的堆内存中。

 

         Erlang的GC机制跟其它语言(比如Java)相比,很重要的一点是,它的GC是以进程为单位进行的(一般情况下,GC搜索的根对象主要包括进程栈以及进程信箱中的对象)。Erlang系统中,GC进行时,会挂起整个系统(当前结点上的所以调度队列),也就是说它的GC也是“stop the world”。但是,就算一个系统中有大量进程,总共占用几个G的内存,它的GC的延迟也会很低,这是因为每个进程可能只使用很小的内存(比如20K),在这么小的内存上进行GC所花费的时间很小,基本可以忽略不计。

在Erlang中可以通过spawn_opt来指定初始堆内存大小,如果这个数值足够大(需要诊断后确定),那么就可以完全避免GC。进程在销毁时,统一收回其所拥有的所有堆内存,而不需要进行GC,因为堆内存是每个进程私有的。

 

         Erlang中,堆内存被分成年轻代和年老代。进程在分配新数据时,会将数据放在年轻代中(分代的理论基础是大部分刚创建的对象会在不久的将来不再使用)。只查看年轻代对象的GC称为minor GC,查看所有堆内存对象的GC称为major GC,因此minor GC比major GC更频繁,也更快。年轻代中的对象经过两到三次minor GC后,才会被拷贝到年老代。

 

Minor GC

         年青代堆内存中存在一个标记位:高水位线(high water mark),比这个标记位地址小的对象称为较老的年轻代(older young generation),大于这个地址的称为较年轻的年轻代(younger young generation)。

         Minor GC时,会分配一块新的堆内存,用于存放在可以在本次GC后可以生存下来的较年轻的年轻代。年轻代内存中被进程的根对象所引用的对象,如果它的地址小于高水位线,则会被拷贝到年老代堆内存,否则将其拷贝到新分配的堆内存中。然后根据新分配堆内存以及较老的年轻代堆内存中对象的引用,将所有活着的对象按原来的位置关系拷贝到新分配的堆内存或者年老代中。

         Minor GC进行时,年老代堆内存不会被扫描,以加快GC速度。Minor GC完成后,会根据GC过程中是否有较老的年轻代对象来决定高水位线的位置:如果存在,高水位线设置成

新的年轻代堆内存的开始地址;如果不存在,则会设置成年轻代堆内存的堆顶地址。

 

 

Major GC

         Major GC时,年轻代堆内存和年老代堆内存中被根对象间接或直接引用到的对象都会被拷贝到新的堆内存中,高水位线会被设置成新的堆内存的堆顶地址。新的堆内存成为当前进程的年轻代堆,原来的老的堆内存(年轻代以及年老代)都会被释放。

 


标签
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!