首页 | 新闻 | 新品 | 文库 | 方案 | 视频 | 下载 | 商城 | 开发板 | 数据中心 | 座谈新版 | 培训 | 工具 | 博客 | 论坛 | 百科 | GEC | 活动 | 主题月 | 电子展
返回列表 回复 发帖

对比Ruby和Python的垃圾回收

对比Ruby和Python的垃圾回收

注:这篇文章基于我在布达佩斯的RuPy大会上所作的演讲。我觉得与其直接将幻灯片发布出来,不如在我还有印象的时候将它写成博客来的更有意义。同样,我会在将来发布RuPy大会的视频链接。我计划将在RubyConf大会上发表类似的演讲,除了有关于Python的部分,并且将对比MRI,JRuby以及Rubinius的垃圾回收器是怎样工作的。
如果想要对Ruby垃圾回收器以及内部原理有更加深入的了解,你可以在我即将出版的新书《Ruby Under a Microscope》中找到答案。
如果算法和业务逻辑是一个人的大脑,那么垃圾回收机制是人体的哪个器官呢?
在”Ruby Python”大会上,我想对比Ruby和Python内部的垃圾回收机制是一件很有意思的事情。在开始之前,我们为什么要讨论垃圾回收机制呢?毕竟这是一个最迷人的,最令人激动的主题,不是吗?你们有多少人对垃圾回收机制感到兴奋?(许多的大会参与者竟然举起了双手!)
最近,在Ruby社区中有一篇帖子,关于怎样通过修改Ruby GC的设置来提高单元测试的速度。这棒极了!通过减少GC垃圾回收的处理来提高测试的速度,这是一件很好的事情,但是不怎的,GC不会真正的让我感到兴奋。就如咋一看就感觉令人厌烦,枯燥的技术帖子。
事实上,垃圾回收是一个令人着迷的主题:垃圾回收算法不仅是计算机科学历史一个重要的部分,更是前沿研究的一个主题。例如,MRI Ruby解释器使用的”Mark Sweep”算法已经超过了50年的历史,与此同时,在Rubinius解释器中使用的一种垃圾回收算法,是在Ruby中的另一种实现方式,这种算法仅仅是在2008才被研究出来。
然而,”垃圾回收”的这个名称,是非常的不恰当的。
应用程序的心脏
垃圾回收系统要做的不仅仅是”回收垃圾”。事实上,它主要完成三个重要任务:
为新的对象分配内存
标记垃圾对象
回收垃圾对象占用的内存
想象你的应用程序是一个人的身体:所有你写的优雅的代码,你的商业逻辑,你的算法,将会成为你的应用程序的大脑或智能。与此类似的,你认为垃圾回收器会成为身体的哪一个部分呢?(我从大会的听众中得到了很多有趣的答案:肾,白细胞)
我认为垃圾回收器是一个应用的心脏。正如心脏为身体的其他部分提供血液和养料一样,垃圾回收器提供内存和对象供程序使用。如果你的心脏停跳,你将活不了几秒。如果垃圾回收器停止运行或者变慢,就像动脉阻塞一样,你的程序将变的慢下来最后死掉!
一个简单的例子
通过例子来验证理论是一种很好的方式。这里有一个简单的类,用Python和Ruby写成,我们可以将它们作为一个简单的例子:
于此同时,两种代码如此相似让我感到非常吃惊:Python和Ruby在表达相同的语义时几乎没有差别。但是,两种语言的内部实现方式是否相同呢?
空闲对象链表
在上面的代码中,当我们调用了Node.new(1)之后,ruby将会做什么?也就是说,Ruby怎样创建一个新的对象?
令人惊讶的是,Ruby做的事情非常少!事实上,在代码运行之前,Ruby解释器会提前创建成千上万的对象放置到一个链表中,这个链表被称为”空闲对象链表”(free list)。空闲对象链表(`free list`)在概念上看起来像下面的样子:
每一个白色方块可以想象成一个预创建的,没有使用的Ruby对象。当我们调用Node.new,Ruby简单的使用一个对象,并且将它的引用返回给我们:
在上图中,左边的灰色方块代表一个活跃的Ruby对象,被我们的代码所使用,而其余的白色方块代码没有使用的对象。(注意:当然,图中是一种简化的实现版本。事实上,Ruby将会使用另外一个对象保存字符串”ABC”,使用第三个对象保存Node的定义,以及其他的对象保存代码处理过的抽象语法数”AST”,等待。)
如果我们再次调用Node.new,Ruby仅仅返回另外一个对象的引用。
约翰麦卡锡在1960年在Lisp中首次实现了垃圾回收机制
这中使用预创建对象链表的简单算法发明于50多年前,它的作者是传说中的计算机科学家,约翰麦卡锡,正是他实现了最初的Lisp解释器。Lisp不仅是第一个函数式编程语言,并且包含了计算机科学中许多突破性的进展。其中之一便是通过垃圾回收机制自动管理内存。
标准版Ruby,也就是”Matz’s Ruby Interpreter”(MRI),使用了一种类似于约翰麦卡锡在1960年实现的Lisp的垃圾回收算法。就像Lisp一样,Ruby会预先创建对象并且在你创建对象或值的时候返回对象的引用。
在Python中分配对象内存
从上面我们可以看出,Ruby会预先创建对象,并且保存在空闲对象链表(free list)中。那么Python呢?
当然Python内部也会由于各种原因使用空闲对象链表(它使用链表循环确定对象),Python为对象和值分配内存的方式常常不同于Ruby。
假设我们创建一个Node对象使用Python:
Python不同于Ruby,当你创建对象的时候,Python会立即向操作系统申请分配内存。(Python 事实上实现了自己的内存分配系统,它在操作系统内存堆上提供了另外一层抽象,但是今天没有事件深入探讨。 )
当我们创建第二个对象时,Python将再次向操作系统申请更多的内存:
看起来相当简单,当我们创建Python对象的时刻,将花费事件申请内存。
Ruby将没有用的对象扔的到处都是,直到下一个垃圾回收过程
Ruby开发者生活在一个脏乱的房间
返回列表