erlang内存管理

垃圾回收器的本质实际上是改变存活数据结构构成图的连通性.
堆对象在图中的存活性是由指针的可到达性定义的.

程序可以操作三种位置的数据:寄存器 程序栈(局部变量 临时变量) 全局变量.
这些位置的变量有一部分保存了指向堆数据的引用,他们构成了应用程序的根(Root).

对于用户程序动态分配的内存只能通过Root或者根发出的指针链访问,程序不应该访问其地址空间的随机位置.

   内存分配的解决方案有:
    [1] 静态分配:内存在程序编译的时候已经分配好,并且在程序的整个运行期间都存在
    [2] 栈分配
    [3]堆分配 闭包成为可能 递归结构的表达式成为可能


引用计数:

标记清除:每一块数据都被加上一个标签。不定期的,所有标签都被设置为 0,收集器从“基本”的元素开始遍历数据。当它遇到内存时,就将标签标记为 1。最后没有被标记为 1 的所有内容都认为是垃圾,以后分配内存时会重新使用它们。
   缺点:全局标记清除的代价较高,GC停顿时间.

复制:将堆分成两个半区,一个包含现有数据,另一个包含已经被废弃的数据,运行时两个半区的角色不断交换;  缺点:内存浪费较大;

分代回收:是基于统计学原理的:多数内存块的生存周期都比较短,垃圾收集器应当把更多的精力放在检查和清理新分配的内存块上。

"The current default GC is a "stop the world" generational mark-sweep collector. "

erlang process 包含的资源:

-> Process Control Block
    -> ProcessID
    -> Position of Stack and Heap
    -> argument registers
    -> program counter

-> Stack
    -> Simple Data
    -> Reference(Pointer)

-> Private Heap
    -> Tuple
    -> Lists
    -> Big integer


分代,Erlang使用旧数据堆'old heap'来存储存至少经历了一次垃圾回收的数据.当旧数据堆'old heap'没有足够的空间的时候就会进行一次充分的垃圾回收(fullsweep).

创建进程的时候我们可以通过使用spawn_opt/4来设置fullsweep_after参数,这个参数的意思是:最多经过多少代就可以强制进行充分垃圾回收了,不管旧数据堆是否有剩余空间.

分代(generational )本身是基于统计学的:多数内存块的生存周期都比较短,最近创建的对象更容易变冷(不再被使用).

Erlang对"年轻"一代对象的GC会更频繁,减少对常驻内存的对象GC次数.

对于一个Erlang进程当没有足够Heap空间的时候就会触发GC.由于Heap是私有的所以进程销毁的时候内存可以直接回收.

Erlang的GC可以分成两种:minor collection and major collection.
Minor collection只对年轻一代(young generation)的对象进行GC,Major collection会进行整体GC. 在进行了一定次数Minor Collection后,或者Minor Collection没能释放足够的内存的时候就会触发Major collection.

每一个Erlang进程创建之后都会有自己的PCB,栈,私有堆.
Erlang进程结束的时候,内存资源理解被释放便于资源复用.

这样做背后的思想是:每一个进程都只有一小部分活跃数据(live data),所以垃圾回收将会是一个很快的操作.换句话说Erlang的垃圾回收是以进程为单位的,虽然GC过程会进程挂起但是由于回收速度快,影响很小.


ETS是一个全局数据库,可以被节点内的所有进程共享访问.
ETS也是由进程实现,所以存储和查询数据和消息发送一样都是通过复制实现.

Erlang二进制数据通常数据量相当大,如果二进制数据<64 bytes会在进程内存储,如果超过64 bytes二进制数据是在进程以外的独立的堆分配.

二进制数据占用一块数据区域,数据区域头信息包含指向数据区域的指针.当二进制数据分割成子二进制数据段的时候,会创建新的数据头信息但数据并没有被拷贝.

二进制内存分配对节点内所有的Erlang进程可见.发送消息的时候,二进制数据发送的是引用.如果是跨节点发送二进制数据当然还是通过拷贝实现的.

尽管垃圾回收器是基于拷贝的,二进制数据是走的标记-清除(mark-sweep)的路子.我们知道,标记-清除已经是面向全局的垃圾回收机制了.


原子是不会被垃圾回收器回收的。一旦一个原子被创建,它将永远不会被移除。如果原子的数量达到了限制数量(默认是1048576),模拟器会终止。

在一个持续运转的系统中,将任意的字符串输入转换为原子是危险的。

如果只允许输入某些良好定义的原子,你可以使用list_to_existing_atom/1函数(所有被允许的原子必须先创建好,比如在一个模块中使用,然后再加载那个模块)

apply(list_to_atom("some_prefix"++Var), foo, Args)
  is quite expensive and is not recommended in time-critical code.

参考:
http://www.cnblogs.com/me-sa/archive/2011/11/13/erlang0014.html

猜你喜欢

转载自catdoc.iteye.com/blog/2111510