【发布时间】:2012-04-30 14:28:54
【问题描述】:
【问题讨论】:
标签: memory-management garbage-collection erlang
【问题讨论】:
标签: memory-management garbage-collection erlang
算法的参考论文:One Pass Real-Time Generational Mark-Sweep Garbage Collection (1995) 由 Joe Armstrong 和 Robert Virding 在 1995 年(在 CiteSeerX)
摘要:
传统的标记清除垃圾收集算法在算法的标记阶段终止之前不允许回收数据。对于不允许破坏性操作的语言类,我们可以安排堆中的所有指针总是向后指向“旧”数据。在本文中,我们提出了一个简单的方案,用于使用单通道标记扫描收集器为此类语言类回收数据。我们还展示了如何修改简单方案,以便可以增量方式完成收集(使其适用于实时收集)。在此之后,我们展示了如何修改收集器以进行分代垃圾收集,最后该方案如何用于具有并发进程的语言。1
【讨论】:
我不知道你的背景,但是除了jj1bdx已经指出的论文之外,你也可以给Jesper Wilhelmsson thesis一个机会。
顺便说一句,如果您想监控 Erlang 中的内存使用情况以将其与例如C++ 你可以看看:
希望这会有所帮助!
【讨论】:
Erlang 有一些特性让 GC 实际上非常容易。
1 - 每个变量都是不可变的,因此变量永远不能指向在它之后创建的值。
2 - 在 Erlang 进程之间复制值,因此进程中引用的内存几乎总是完全隔离的。
这两者(尤其是后者)都极大地限制了 GC 在收集期间必须扫描的堆数量。
Erlang 使用复制 GC。在 GC 期间,进程停止,然后将活动指针从 from-space 复制到 to-space。我忘记了确切的百分比,但是如果在收集过程中只能收集 25% 的堆,那么堆将会增加,如果可以收集 75% 的进程堆,那么堆将会减少。当进程堆满时触发收集。
唯一的例外是发送到另一个进程的大值。这些将被复制到共享空间并被引用计数。当收集到共享对象的引用时,计数会减少,当该计数为 0 时,对象会被释放。没有尝试处理共享堆中的碎片。
这样做的一个有趣结果是,对于共享对象,共享对象的大小不会影响进程堆的计算大小,只有引用的大小会影响。这意味着,如果您有很多大型共享对象,您的 VM 可能会在触发 GC 之前耗尽内存。
大部分内容来自 Jesper Wilhelmsson 在 EUC2012 上的演讲。
【讨论】:
large value 有多大?
为了对事物进行分类,我们先定义内存布局,然后谈谈 GC 的工作原理。
在 Erlang 中,每个执行线程都称为一个进程。每个进程都有自己的内存,内存布局由三部分组成:Process Control Block、Stack和Heap。
PCB: 进程控制块保存进程标识符 (PID)、当前状态(运行、等待)、其注册名称和其他此类信息。
李>堆栈:它是一个向下增长的内存区域,用于保存传入和传出参数、返回地址、局部变量和用于评估表达式的临时空间。
堆:它是一个向上增长的内存区域,用于保存进程邮箱消息和复合术语。大于 64 字节的二进制项不存储在进程私有堆中。它们存储在一个可供所有进程访问的大型共享堆中。
目前 Erlang 使用 Generational 垃圾收集,在每个 Erlang 进程私有堆内独立运行,并且对全局共享堆进行 Reference Counting 垃圾收集。
私有堆 GC: 它是分代的,因此将堆分为两部分:年轻代和老年代。还有两种收集策略;世代(次要)和全扫描(主要)。分代GC只收集年轻堆,而全扫描收集年轻堆和老堆。
共享堆GC:是引用计数。共享堆(Refc)中的每个对象都有一个由其他对象(ProcBin)持有的对它的引用计数器,这些对象存储在 Erlang 进程的私有堆中。如果对象的引用计数器达到零,则该对象已变得不可访问并将被销毁。
要获得更多细节和性能提示,请看我的文章,这是答案的来源:Erlang Garbage Collection Details and Why It Matters
【讨论】: