erlang二进制数据垃圾回收

原文 2014-06-10 01:01:57 发表于 CSDN,这里对以前写的文章做下收录。

erlang 二进制数据在内存中有两种存在形式,当数据大小不到 64 bytes,就直接存在进程堆内,如果超过了 64 bytes,就被保存到进程外的共享堆里,可以给节点内所有进程共享。

erlang 有两种二进制容器:heap binaries 和 refc binaries。
heap binaries

Heap binaries are small binaries, up to 64 bytes, that are stored directly on the process heap. They will be copied when the process is garbage collected and when they are sent as a message. They don't require any special handling by the garbage collector.

这个就是进程堆二进制,是一些比较小的二进制数据,每个数据大小不超过 64bytes,这些数据保存在进程堆内。对于这里的二进制数据,垃圾回收走的是进程堆数据的回收机制,参考这里。如果发给其他进程的消息含有这些数据,erlang 将直接复制一份到别的进程堆内。

针对 heap binaries,在 R13B03 后,erlang 还增加了 bin vheap 来加快二进制数据的回收。

OTP-8202  A new garbage collecting strategy for binaries which is more aggressive than the previous implementation. Binaries now has a virtual binary heap tied to each process. When binaries are created or received to a process it will check if the heap limit has been reached and if a reclaim should be done. This imitates the behavior of ordinary Erlang terms. The virtual heaps are grown and shrunk like ordinary heaps. This will lessen the memory footprint of binaries in a system.

就是说 heap binaries 的垃圾回收使用了进程堆数据的回收方式,但使用了一个虚拟二进制堆 (vheap) 来计算这些二进制的使用情况,加快内存回收速度。

refc binaries

Refc binaries consist of two parts: an object stored on the process heap, called a ProcBin, and the binary object itself stored outside all process heaps.The binary object can be referenced by any number of ProcBins from any number of processes; the object contains a reference counter to keep track of the number of references, so that it can be removed when the last reference disappears.

官方的叫法是引用计数二进制,就是对于那些超过 64bytes 的二进制数据,他们如果直接保存在进程堆内,将导致进程频繁的 gc,比较大的数据复制来复制去开销也很大。erlang 将这些数据保存在进程外的共享堆,再把这个二进制数据的地址给拥有这个数据的进程。所以,进程堆内保存的是这个二进制数据的引用,叫 ProcBin。如果进程把这个二进制数据发给其他进程,erlang 也不再复制整个二进制数据,而是直接再生成一份 ProcBin 到别的进程堆内。那么,这个二进制数据就可以多个进程的 ProcBin 引用,当没有一个 ProcBin 引用到这个二进制数据,这个二进制就被 erlang 回收。
所以,这种二进制数据的 gc 的是引用计数的回收机制。注意了,ProcBin 是进程堆内数据,走的是进程堆数据的回收方式。

什么是引用计数垃圾回收机制
引用计数就是每个数据对象都配有一个计数器,计算对象被引用的次数。多了一个引用就加 1,少一个引用就减 1,当引用次数为 0 时就回收数据。这种垃圾回收机制实现简单,回收及时,但也有副作用,就是容易造成循环引用,就是 A 引用 B,B 引用 C,C 引用了 A,导致 A、B、C 都无法被回收。
那么,erlang 会不会也有循环引用的问题?这里就不用担心,erlang 的变量是单向赋值,只存在 ProcBin 对二进制对象的单向引用

最后,说下erlang另外两种二进制数据:sub binary和match context

A sub binary is created by split_binary/2 and when a binary is matched out in a binary pattern. A sub binary is a reference into a part of another binary (refc or heap binary, never into a another sub binary). Therefore, matching out a binary is relatively cheap because the actual binary data is never copied.
A match context is similar to a sub binary, but is optimized for binary matching; for instance, it contains a direct pointer to the binary data. For each field that is matched out of a binary, the position in the match context will be incremented.

前面谈到 erlang 为避免二进制数据复制带来的时间和空间的开销,erlang 这里做得更彻底一点,sub binary 和 match context 其实是引用对象,被用来引用 heap binary 和 refc binary 的数据

说到 sub binary 和 match context,这两者有什么区别?
sub binary 是一个子二进制数据,从一个二进制分割出来,或匹配一个二进制后产生,具有二进制数据通用的属性和方法;match context 是匹配上下文,在 erlang 进行二进制数据匹配时产生,如果接下来使用了匹配到的二进制数据,那么 erlang 就将这个 match context 数据转成 sub binary,就是说,match context 数据不直接被用户使用,只是 erlang 用以二进制匹配优化的过程数据

erlang二进制gc的副作用
从上面的内容可以知道,erlang 二进制 gc 有两种 gc,heap binary 的是进程堆的分代 gc,refc binary 的是分代 gc + 引用计数。所以在上文讲到的 erlang 垃圾回收的副作用,这里同样会有。而且,erlang 二进制还加入了引用对象的概念,一个二进制数据可以有多个引用,可以被多个进程引用,也就是要多个进程堆的分代 gc 后才能回收,这就使得二进制 gc 更难控制,回收不及时。
另外,在实际的网络开发中,我们需要的数据可能只是二进制数据的一小部分,从上面的内容也可以了解到,如果我们还在使用二进制数据的一部分,那这个二进制数据是不会参与 gc 的。针对这个问题我们该如何解决?

%% 生成一个100字节的二进制A
1> A = binary:copy(<<1>>,100).
<<1,1,1,1,1 ...
2> byte_size(A).
100
3> binary:referenced_byte_size(A).
100

%% 匹配二进制,产生子二进制B
4> <<_:10/binary,B:10/binary,_/binary>> = A.
<<1,1,1,1,1 ...
5> byte_size(B).
10
6> binary:referenced_byte_size(B).
100

%% 复制二进制生成二进制C
7> C = binary:copy(B).
<<1,1,1,1,1,1,1,1,1,1>>
8> binary:referenced_byte_size(C).
10

上面,二进制 B 是二进制 A 的子二进制,如果 B 还有使用,A 就不会参与 gc;而二进制 C 是一个新的二进制,不会引用二进制 A,让 A 可以参与垃圾回收

更新说明:
2014/06/13 补充了 erlang 二进制垃圾回收的副作用
参考:
http://www.erlang.org/doc/efficiency_guide/binaryhandling.html#id65798​

发表评论

邮箱地址不会被公开。 必填项已用*标注