声明:本片文章是由Hackernews上的[Erlang Garbage Collection Details and Why It
Matters][1]编译而来,本着学习和研究的态度,进行的编译,转载请注明出处。

Erlang需要解决的重要问题之一就是为实现极高响应能力的软实时系统创建平台。这样的系统需要一个快速的垃圾回收机制,而这个机制不会阻止系统及时的响应。另一方面,当我们把Erlang看作一种用无损更新属性的不可改变语言时,这个垃圾回收机制就显得更加重要了,因为这种语言有很高的几率产生垃圾。

内存布局

在深入了解GC之前,有一个很重要的事,就是检查Erlang过程的内存布局的三个重要的点:进程控制模块,栈和堆。它和Unix的内存布局非常的相像。

进程控制模块:进程控制模块会保存一些关于进程的信息比如它在进程表中的标识符(PID)、当前状态(运行、等待)、它的注册名、初始和当前调用,同时PCB也会保存一些指向传入消息的指针,这些传入消息是存储在堆中连接表中的。

栈:它是一个向下生长的存储区,这个存储区保存输入和输出参数、返回地址、本地变量和用于evaluating expressions的临时空间。

堆:它是一个向上生长的存储区,这个存储区保存进程邮箱的物理消息,像列表、元组和Binaries这种的复合项以及比像浮点数这种一个机器字更大的对象。超过64机器字的二进制项不会存储在进程私有堆里。他们被称作Refc Binary (Reference Counted Binary)并被存储在一个大的共享堆里,只要有那个Refc Binary指针的进程都可以访问这个堆。这个储存在进程私有堆中的指针叫作ProcBin。

GC的细节

为了结实当前默认Erlang的GC机制,简单的说,它是一个分代复制的垃圾回收,独立运行在每个Erlang进程私有堆的内部,而且它也是发生在全球共享堆中的引用计数垃圾回收。

私有堆GC

私有堆的GC是分代的。分代GC把堆分为了新生和老年代两个部分。如果一个对象在GC循环生存下来,那么它在短期内成为垃圾的几率将会很低,这也是这个划分的依据所在。因此,新生代是为新分配的数据准备的,老年代是为了在数次GC启动后生存下来数据的。这个分代帮助了GC减少在还没有成为垃圾数据上的不必要的循环。对于Erlang垃圾回收有两个策略:Generational (Minor)和Fullsweep (Major)。分代的GC只收集新生的堆,而fullsweep的堆新老都会收集。现在,让我们回顾一个新开始Erlang进程私有堆的GC步骤:

场景1:

Spawn > No GC > Terminate

如果一个短暂的进程没有使用超过min_heap_size的堆就结束了,GC是不会发生的。这种情况下所有被进程使用过的内存会被收集。

场景2:

Spawn > Fullsweep > Generational > Terminate

如果一个新生产的进程的数据增长超过min_heap_size,那么会使用fullsweep GC,显然这是因为没有GC发生,那么也不会有新生代和老年代之分。在第一次fullsweep GC后,堆就会被分代成这两部分,之后GC策略会转化到分代并保持到进程结束。

场景3:

Spawn > Fullsweep > Generational > Fullsweep > Generational > ... >
Terminate

有几种情况,GC策略在进程过程中由分代转化回到fullsweep。第一种情况是进过一定次数的分代GC。这个数量可以是特定全局的或者是每个有fullsweep_after flag的进程。同时在fullsweep GC之前每个的进程和它的上限的分代GC计数器分别是minor_gcs 和 fullsweep_after特性,并在process_info(PID, garbage_collection)返回值中可见。第二种情况是当分代GC不能收集到足够的内存,最后一种情况是garbage_collect(PID)函数被手动调用。在这些情况后,GC策略会回复到从fullsweep到分代然后保持直到上述情形发生。

场景4:

Spawn > Fullsweep > Generational > Fullsweep > Increase Heap >
Fullsweep > ... > Terminate

在场景3中,如果第二fullsweep GC不能收集到足够内存,堆的大小会增加,GC策略又会转化成fullsweep,就像新生成的进程一样,这四种场景可以不断的出现。

现在的问题是为什么在像Erlang这种自动垃圾收集语言这么重要。首先这些知识可以帮助你通过调整GC的发生和策略使你的系统运行更快。其次,这是我们明白从垃圾回收的角度使Erlang变成软件实时平台的重要原因的地方。这是因为每个进程都有它自己的堆和它自己的GC,所以每次GC出现在一个进程中的时候,只是停止正在收集过程中的Erlang进程,但不会停止其他的进程,而这正是一个软实时系统所需要的。

共享堆GC

共享堆的GC是参考计数。每个在共享堆(Refc)的对象都有与存储的其他对象(ProcBin)相对的参考计数器,这些其他对象(ProcBin)都存储在Erlang进程私有堆内部。如果一个对象参考计数器达到0,这个对象会变得无法访问并将销毁。参考计数器很廉价并且可以帮助系统避免意外长时的暂停而且提高体统的响应速度。但是在设计你的actor模型系统时,不了解一些著名的反模式会导致一些问题,比如内存泄漏

当Refc第一次分成一个Sub-Binary。为了降低成本,一个sub-binary不是一个原binary分裂部分的新副本,仅仅是那个部分的一个参考。然而这个sub-binary会被当作加入到原binary的的一个新的参考,你知道,当原binary必须挂在它的sub-binary上时,这可能会引起一些问题。

其他已知的问题会发生在当一种生命周期很长的中间件当作控制和传递大型Refc binary消息的请求控制器或消息路由器时。当这个进程接触到每个Refc消息时,它们的计数器会递增。因此收集这些Refc消息依靠于收集所有ProcBin对象,即使它们在中间件进程中。不幸的是,因为ProcBin仅仅只是个指针,因此它们成本很低而且在中间件进程中需要花很长的时间去触发GC。所以即使已经从除了中间件其他所有进程中收集了Refc消息,它们也需要保留在共享堆里。

共享堆之所以重要是因为它减少了由于在进程之间传递大量binary消息的IO。由于sub-binaries仅仅是其他binary的指针,他们可以快速的创建。但是作为一种经验法则,使用变得更快的捷径会产生成本,这个成本会以一种不会在恶劣条件下困住方式去构建你的系统。同时也有很多应对Refc binary泄露的著名方法,比如Fred Hebert在他的ebook发表的Erlang in Anger。我认为我不能解释的比他更好,所以强烈推荐你去阅读。

总结:

即使我们使用像Erlang这种自我管理内存的语言,了解内存是如何分配和释放也是很必要的。不像Go的内存模型文档建议你“如果你必须要通过阅读剩下的文档去了解你的编程的行为,那么你太聪明了。不要这么聪明”,我相信我们必须要足够的聪明去让我们的系统运行得更快更安全,但做到这一点,深入了解它的原理是必不可少的。

转自: 云巴专栏 https://segmentfault.com/a/1190000003758525

深入了解Erlang 垃圾回收机制以及其重要性(转)的更多相关文章

  1. Erlang垃圾回收机制的二三事

    声明:本片文章是由Hackernews上的[Erlang Garbage Collection Details and Why ItMatters][1]编译而来,本着学习和研究的态度,进行的编译,转 ...

  2. Erlang进程堆垃圾回收机制

    原文:Erlang进程堆垃圾回收机制 作者:http://blog.csdn.net/mycwq 每一个Erlang进程创建之后都会有自己的PCB,栈,私有堆.erlang不知道他创建的进程会用到哪种 ...

  3. erlang二进制数据垃圾回收机制

    erlang二进制数据在内存中有两种存在形式,当数据大小不到 64 bytes,就直接存在进程堆内.假设超过了64 bytes.就被保存到进程外的共享堆里,能够给节点内全部进程共享. erlang有两 ...

  4. .net垃圾回收机制编程调试试验

    1. 什么是CLR GC? 它是一个基于引用跟踪和代的垃圾回收器. 从本质上,它为系统中所有活跃对象都实现了一种引用跟踪模式,如果一个对象没有任何引用指向它,那么这个对象就被认为是垃圾对象,并且可以被 ...

  5. JavaScript具有自动垃圾回收机制

    JavaScript具有自动垃圾回收机制 原理: 找出那些不再继续使用的变量,然后释放其占用的内存.   正常的生命周期:     局部变量指在函数执行的过程中存在.而在这个过程中,会为局部变量在栈或 ...

  6. java垃圾回收机制

    1 .垃圾回收机制(GC)垃圾回收就是回收内存中不再使用对象:(1)垃圾回收的步骤:1)查找内存中不再使用的对象:2)释放这些对象所占用的内存:(2)查找内存中不再使用的对象方法:1)引用计数法如果一 ...

  7. 垃圾回收机制GC知识再总结兼谈如何用好GC

    一.为什么需要GC 应用程序对资源操作,通常简单分为以下几个步骤: 1.为对应的资源分配内存 2.初始化内存 3.使用资源 4.清理资源 5.释放内存 应用程序对资源(内存使用)管理的方式,常见的一般 ...

  8. 【转载】Java垃圾回收机制

    原文地址:http://www.importnew.com/19085.html Java垃圾回收机制 说到垃圾回收(Garbage Collection,GC),很多人就会自然而然地把它和Java联 ...

  9. 【转】深入理解 Java 垃圾回收机制

    深入理解 Java 垃圾回收机制   一.垃圾回收机制的意义 Java语言中一个显著的特点就是引入了垃圾回收机制,使c++程序员最头疼的内存管理的问题迎刃而解,它使得Java程序员在编写程序的时候不再 ...

随机推荐

  1. [Contest20180418]数学竞赛

    题意:初始时$x=0$(长度),当$x$为长度时,你可以把$x$变成$\sin^{-1}x,\cos^{-1}x,\tan^{-1}x$之一($x$变为角度),若$x$为角度,你可以把$x$变成$\s ...

  2. Java 根据年月日精确计算年龄

    import java.text.SimpleDateFormat; import java.util.Calendar; import java.util.Date; /** * Created b ...

  3. Oracle多用户对一个表进行并发插入数据行操作

    oracle数据库支持多用户间同时对同一个表进行操作,但是数据不一定同步,因为oracle数据库是支持脏数据的,比如A用户删除了表的数据但没有提交,B用户也能查询访问到,如果要避免这种情况只能加锁,A ...

  4. 在K8s中创建StatefulSet

    在K8s中创建StatefulSet 遇到的问题: 使用Deployment创建的Pod是无状态的,当挂在Volume之后,如果该Pod挂了,Replication Controller会再run一个 ...

  5. samba 服务实现在windows共享文件

    1. 什么是samba Samba服务类似于windows上的共享功能,可以实现在Linux上共享文件,windows上访问,当然在Linux上也可以访问到. 是一种在局域网上共享文件和打印机的一种通 ...

  6. Mybatis通用分页

    分页分为真分页和假分页,而 MyBatis 本身没有提供基于数据库方言的分页功能,而是基于 JDBC 的游标分页,很容易出现性能问题.网上提供的一个解决方案感觉还不错,是基于 MyBatis 本身的插 ...

  7. oracle分页sql模板

    select t2.* from (select t1.*,rownum rn from (select * from mytest) t1 where rownum<=860010) t2 w ...

  8. Windows 10 Enterprise LTSB版本

    Windows 10 Enterprise 评估版  |  90 天  Win10 企业版 LTSB版 14393.0.160715-1616.RS1_RELEASE_CLIENTENTERPRISE ...

  9. SpringMVC处理MYSQL BLOB字段的上传

    任务: uos.docfile的content字段是longblob类型的,通过页面将文件存储到这个字段里. 页面代码: <div class="box"> <d ...

  10. 黑马程序猿——JAVA高新技术——反射

    ----------android培训.java培训.java学习型技术博客.期待与您交流!------------ 一.对于反射的概念 对于JAVA反射机制是在执行状态中,对于随意一个类.都可以知道 ...