﻿<?xml version="1.0" encoding="utf-8" standalone="yes"?><rss version="2.0" xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:trackback="http://madskills.com/public/xml/rss/module/trackback/" xmlns:wfw="http://wellformedweb.org/CommentAPI/" xmlns:slash="http://purl.org/rss/1.0/modules/slash/"><channel><title>C++博客-Oo缘来是你oO-随笔分类-【03】技术天地</title><link>http://www.cppblog.com/majianan/category/2284.html</link><description>&lt;br&gt;
&lt;br&gt;
人本是人，不必刻意去做人；世本是世，无须精心去处世；自然的才是快乐的。
&lt;br&gt;&lt;br&gt;
</description><language>zh-cn</language><lastBuildDate>Tue, 03 Feb 2009 10:57:09 GMT</lastBuildDate><pubDate>Tue, 03 Feb 2009 10:57:09 GMT</pubDate><ttl>60</ttl><item><title>垃圾收集趣史（转载）</title><link>http://www.cppblog.com/majianan/archive/2006/11/04/14666.html</link><dc:creator>马嘉楠</dc:creator><author>马嘉楠</author><pubDate>Sat, 04 Nov 2006 08:19:00 GMT</pubDate><guid>http://www.cppblog.com/majianan/archive/2006/11/04/14666.html</guid><wfw:comment>http://www.cppblog.com/majianan/comments/14666.html</wfw:comment><comments>http://www.cppblog.com/majianan/archive/2006/11/04/14666.html#Feedback</comments><slash:comments>2</slash:comments><wfw:commentRss>http://www.cppblog.com/majianan/comments/commentRss/14666.html</wfw:commentRss><trackback:ping>http://www.cppblog.com/majianan/services/trackbacks/14666.html</trackback:ping><description><![CDATA[<p><font face=Verdana size=2><font color=#000080 size=4>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 垃圾收集趣史（转）</font> <br></font></p>
<p><font face=Verdana size=2>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 王咏刚，2003年12月<br><br></font><br>写作本文的初衷是想和大家分享垃圾收集（ Garbage Collection ）技术简单而有趣的发展史。动笔之前，我站在窗边，望了望正在小区里装运垃圾的清洁车。和生活中环卫工人们清运垃圾的工作相似，软件开发里的垃圾收集其实就是一种自动打扫和清除内存垃圾的技术，它可以有效防范动态内存分配中可能发生的两个危险：因内存垃圾过多而引发的内存耗尽（这和生活垃圾堵塞排污管道的危险并没有什么本质的不同），以及不恰当的内存释放所造成的内存非法引用（这类似于我们在生活中买到了一瓶已经过期三年的牛奶）。 </p>
<p><font face=Verdana size=2>据历史学家们介绍，四千多年前的古埃及人已经在城市里建设了完善的排污和垃圾清运设施，一千多年前的中国人更是修筑了当时世界上保洁能力最强的都市 ——长安。今天，当我们在软件开发中体验自动垃圾收集的便捷与舒适时，我们至少应当知道，这种拒绝杂乱、追求整洁的&#8220;垃圾收集&#8221;精神其实是人类自古以来就已经具备了的。 </font></p>
<div class=section id=S01><a name=S01></a>
<h3>&nbsp;</h3>
<h3><font face=Verdana color=#000080 size=3>拓荒时代</font> </h3>
<p><font face=Verdana color=#000080>-------------------------------------------------------------------------------------------------------</font> </p>
<p><font face=Verdana size=2>国内的程序员大多是在 Java 语言中第一次感受到垃圾收集技术的巨大魅力的，许多人也因此把 Java 和垃圾收集看成了密不可分的整体。但事实上，垃圾收集技术早在 Java 语言问世前 30 多年就已经发展和成熟起来了， Java 语言所做的不过是把这项神奇的技术带到了广大程序员身边而已。 </font></p>
<p><font face=Verdana size=2>如果一定要为垃圾收集技术找一个孪生兄弟，那么， Lisp 语言才是当之无愧的人选。 1960 年前后诞生于 MIT 的 Lisp 语言是第一种高度依赖于动态内存分配技术的语言： Lisp 中几乎所有数据都以&#8220;表&#8221;的形式出现，而&#8220;表&#8221;所占用的空间则是在堆中动态分配得到的。 Lisp 语言先天就具有的动态内存管理特性要求 Lisp 语言的设计者必须解决堆中每一个内存块的自动释放问题（否则， Lisp 程序员就必然被程序中不计其数的 free 或 delete 语句淹没），这直接导致了垃圾收集技术的诞生和发展——说句题外话，上大学时，一位老师曾告诉我们， Lisp 是对现代软件开发技术贡献最大的语言。我当时对这一说法不以为然：布满了圆括号，看上去像迷宫一样的 Lisp 语言怎么能比 C 语言或 Pascal 语言更伟大呢？不过现在，当我知道垃圾收集技术、数据结构技术、人工智能技术、并行处理技术、虚拟机技术、元数据技术以及程序员们耳熟能详的许多技术都起源于 Lisp 语言时，我特别想向那位老师当面道歉，并收回我当时的幼稚想法。 </font></p>
<p><font face=Verdana size=2>知道了 Lisp 语言与垃圾收集的密切关系，我们就不难理解，为什么垃圾收集技术的两位先驱者 J. McCarthy 和 M. L. Minsky 同时也是 Lisp 语言发展史上的重要人物了。 J. McCarthy 是 Lisp 之父，他在发明 Lisp 语言的同时也第一次完整地描述了垃圾收集的算法和实现方式； M. L. Minsky 则在发展 Lisp 语言的过程中成为了今天好几种主流垃圾收集算法的奠基人——和当时不少技术大师的经历相似， J. McCarthy 和 M. L. Minsky 在许多不同的技术领域里都取得了令人艳羡的成就。也许，在 1960 年代那个软件开发史上的拓荒时代里，思维敏捷、意志坚定的研究者更容易成为无所不能的西部硬汉吧。 </font></p>
<p><font face=Verdana size=2>在了解垃圾收集算法的起源之前，有必要先回顾一下内存分配的主要方式。我们知道，大多数主流的语言或运行环境都支持三种最基本的内存分配方式，它们分别是： </font></p>
<p><font face=Verdana size=2>一、静态分配（ Static Allocation ）：静态变量和全局变量的分配形式。我们可以把静态分配的内存看成是家里的耐用家具。通常，它们无需释放和回收，因为没人会天天把大衣柜当作垃圾扔到窗外。 </font></p>
<p><font face=Verdana size=2>二、自动分配（ Automatic Allocation ）：在栈中为局部变量分配内存的方法。栈中的内存可以随着代码块退出时的出栈操作被自动释放。这类似于到家中串门的访客，天色一晚就要各回各家，除了个别不识时务者以外，我们一般没必要把客人捆在垃圾袋里扫地出门。 </font></p>
<p><font face=Verdana size=2>三、动态分配（ Dynamic Allocation ）：在堆中动态分配内存空间以存储数据的方式。堆中的内存块好像我们日常使用的餐巾纸，用过了就得扔到垃圾箱里，否则屋内就会满地狼藉。像我这样的懒人做梦都想有一台家用机器人跟在身边打扫卫生。在软件开发中，如果你懒得释放内存，那么你也需要一台类似的机器人——这其实就是一个由特定算法实现的垃圾收集器。 </font></p>
<p><font face=Verdana size=2>也就是说，下面提到的所有垃圾收集算法都是在程序运行过程中收集并清理废旧&#8220;餐巾纸&#8221;的算法，它们的操作对象既不是静态变量，也不是局部变量，而是堆中所有已分配内存块。 <br><br><br></font></p>
<div class=section id=S0101>
<h3><font face=Verdana color=#000080 size=3></font>&nbsp;</h3>
<h3><font face=Verdana color=#000080 size=3>引用计数（ Reference Counting ）算法</font> </h3>
<p><font face=Verdana color=#000080>-------------------------------------------------------------------------------------------------------</font> </p>
<p><font face=Verdana size=2>1960 年以前，人们为胚胎中的 Lisp 语言设计垃圾收集机制时，第一个想到的算法是引用计数算法。拿餐巾纸的例子来说，这种算法的原理大致可以描述为： </font></p>
<p><font face=Verdana size=2>午餐时，为了把脑子里突然跳出来的设计灵感记下来，我从餐巾纸袋中抽出一张餐巾纸，打算在上面画出系统架构的蓝图。按照&#8220;餐巾纸使用规约之引用计数版&#8221;的要求，画图之前，我必须先在餐巾纸的一角写上计数值 1 ，以表示我在使用这张餐巾纸。这时，如果你也想看看我画的蓝图，那你就要把餐巾纸上的计数值加 1 ，将它改为 2 ，这表明目前有 2 个人在同时使用这张餐巾纸（当然，我是不会允许你用这张餐巾纸来擦鼻涕的）。你看完后，必须把计数值减 1 ，表明你对该餐巾纸的使用已经结束。同样，当我将餐巾纸上的内容全部誊写到笔记本上之后，我也会自觉地把餐巾纸上的计数值减 1 。此时，不出意外的话，这张餐巾纸上的计数值应当是 0 ，它会被垃圾收集器——假设那是一个专门负责打扫卫生的机器人——捡起来扔到垃圾箱里，因为垃圾收集器的惟一使命就是找到所有计数值为 0 的餐巾纸并清理它们。 </font></p>
<p><font face=Verdana size=2>引用计数算法的优点和缺陷同样明显。这一算法在执行垃圾收集任务时速度较快，但算法对程序中每一次内存分配和指针操作提出了额外的要求（增加或减少内存块的引用计数）。更重要的是，引用计数算法无法正确释放循环引用的内存块，对此， D. Hillis 有一段风趣而精辟的论述： </font></p>
<p><font face=Verdana size=2>一天，一个学生走到 Moon 面前说：&#8220;我知道如何设计一个更好的垃圾收集器了。我们必须记录指向每个结点的指针数目。&#8221; Moon 耐心地给这位学生讲了下面这个故事：&#8220;一天，一个学生走到 Moon 面前说：&#8216;我知道如何设计一个更好的垃圾收集器了&#8230;&#8230;&#8217;&#8221; </font></p>
<p><font face=Verdana size=2>D. Hillis 的故事和我们小时候常说的&#8220;从前有座山，山上有个庙，庙里有个老和尚&#8221;的故事有异曲同工之妙。这说明，单是使用引用计数算法还不足以解决垃圾收集中的所有问题。正因为如此，引用计数算法也常常被研究者们排除在狭义的垃圾收集算法之外。当然，作为一种最简单、最直观的解决方案，引用计数算法本身具有其不可替代的优越性。 1980 年代前后， D. P. Friedman ， D. S. Wise ， H. G. Baker 等人对引用计数算法进行了数次改进，这些改进使得引用计数算法及其变种（如延迟计数算法等）在简单的环境下，或是在一些综合了多种算法的现代垃圾收集系统中仍然可以一展身手。 </font></p>
</div>
<div class=section id=S0102><a name=S0102></a>
<h3>&nbsp;</h3>
<h3><font face=Verdana color=#000080 size=3>标记－清除（ Mark-Sweep ）算法</font> </h3>
<p><font face=Verdana color=#000080>-------------------------------------------------------------------------------------------------------</font> </p>
<p><font face=Verdana size=2>第一种实用和完善的垃圾收集算法是 J. McCarthy 等人在 1960 年提出并成功地应用于 Lisp 语言的标记－清除算法。仍以餐巾纸为例，标记－清除算法的执行过程是这样的： </font></p>
<p><font face=Verdana size=2>午餐过程中，餐厅里的所有人都根据自己的需要取用餐巾纸。当垃圾收集机器人想收集废旧餐巾纸的时候，它会让所有用餐的人先停下来，然后，依次询问餐厅里的每一个人：&#8220;你正在用餐巾纸吗？你用的是哪一张餐巾纸？&#8221;机器人根据每个人的回答将人们正在使用的餐巾纸画上记号。询问过程结束后，机器人在餐厅里寻找所有散落在餐桌上且没有记号的餐巾纸（这些显然都是用过的废旧餐巾纸），把它们统统扔到垃圾箱里。 </font></p>
<p><font face=Verdana size=2>正如其名称所暗示的那样，标记－清除算法的执行过程分为&#8220;标记&#8221;和&#8220;清除&#8221;两大阶段。这种分步执行的思路奠定了现代垃圾收集算法的思想基础。与引用计数算法不同的是，标记－清除算法不需要运行环境监测每一次内存分配和指针操作，而只要在&#8220;标记&#8221;阶段中跟踪每一个指针变量的指向——用类似思路实现的垃圾收集器也常被后人统称为跟踪收集器（ Tracing Collector ） </font></p>
<p><font face=Verdana size=2>伴随着 Lisp 语言的成功，标记－清除算法也在大多数早期的 Lisp 运行环境中大放异彩。尽管最初版本的标记－清除算法在今天看来还存在效率不高（标记和清除是两个相当耗时的过程）等诸多缺陷，但在后面的讨论中，我们可以看到，几乎所有现代垃圾收集算法都是标记－清除思想的延续，仅此一点， J. McCarthy 等人在垃圾收集技术方面的贡献就丝毫不亚于他们在 Lisp 语言上的成就了。 </font></p>
</div>
<div class=section id=S0103><a name=S0103></a>
<h3>&nbsp;</h3>
<h3><font face=Verdana color=#000080 size=3>复制（ Copying ）算法</font> </h3>
<p><font face=Verdana color=#000080>-------------------------------------------------------------------------------------------------------</font> </p>
<p><font face=Verdana size=2>为了解决标记－清除算法在垃圾收集效率方面的缺陷， M. L. Minsky 于 1963 年发表了著名的论文&#8220;一种使用双存储区的 Lisp 语言垃圾收集器（ A LISP Garbage Collector Algorithm Using Serial Secondary Storage ）&#8221;。 M. L. Minsky 在该论文中描述的算法被人们称为复制算法，它也被 M. L. Minsky 本人成功地引入到了 Lisp 语言的一个实现版本中。 </font></p>
<p><font face=Verdana size=2>复制算法别出心裁地将堆空间一分为二，并使用简单的复制操作来完成垃圾收集工作，这个思路相当有趣。借用餐巾纸的比喻，我们可以这样理解 M. L. Minsky 的复制算法： </font></p>
<p><font face=Verdana size=2>餐厅被垃圾收集机器人分成南区和北区两个大小完全相同的部分。午餐时，所有人都先在南区用餐（因为空间有限，用餐人数自然也将减少一半），用餐时可以随意使用餐巾纸。当垃圾收集机器人认为有必要回收废旧餐巾纸时，它会要求所有用餐者以最快的速度从南区转移到北区，同时随身携带自己正在使用的餐巾纸。等所有人都转移到北区之后，垃圾收集机器人只要简单地把南区中所有散落的餐巾纸扔进垃圾箱就算完成任务了。下一次垃圾收集的工作过程也大致类似，惟一的不同只是人们的转移方向变成了从北区到南区。如此循环往复，每次垃圾收集都只需简单地转移（也就是复制）一次，垃圾收集速度无与伦比——当然，对于用餐者往返奔波于南北两区之间的辛劳，垃圾收集机器人是决不会流露出丝毫怜悯的。 </font></p>
<p><font face=Verdana size=2>M. L. Minsky 的发明绝对算得上一种奇思妙想。分区、复制的思路不仅大幅提高了垃圾收集的效率，而且也将原本繁纷复杂的内存分配算法变得前所未有地简明和扼要（既然每次内存回收都是对整个半区的回收，内存分配时也就不用考虑内存碎片等复杂情况，只要移动堆顶指针，按顺序分配内存就可以了），这简直是个奇迹！不过，任何奇迹的出现都有一定的代价，在垃圾收集技术中，复制算法提高效率的代价是人为地将可用内存缩小了一半。实话实说，这个代价未免也太高了一些。 </font></p>
<p><font face=Verdana size=2>无论优缺点如何，复制算法在实践中都获得了可以与标记－清除算法相比拟的成功。除了 M. L. Minsky 本人在 Lisp 语言中的工作以外，从 1960 年代末到 1970 年代初， R. R. Fenichel 和 J. C. Yochelson 等人也相继在 Lisp 语言的不同实现中对复制算法进行了改进， S. Arnborg 更是成功地将复制算法应用到了 Simula 语言中。 </font></p>
<p><font face=Verdana size=2>至此，垃圾收集技术的三大传统算法——引用计数算法、标记－清除算法和复制算法——都已在 1960 年前后相继问世，三种算法各有所长，也都存在致命的缺陷。从 1960 年代后期开始，研究者的主要精力逐渐转向对这三种传统算法进行改进或整合，以扬长避短，适应程序设计语言和运行环境对垃圾收集的效率和实时性所提出的更高要求。 </font></p>
</div>
</div>
<div class=section id=S02><a name=S02></a>
<h3>&nbsp;</h3>
<h3><font face=Verdana color=#000080 size=3>走向成熟</font> </h3>
<p><font face=Verdana color=#000080>-------------------------------------------------------------------------------------------------------</font> </p>
<p><font face=Verdana size=2>从 1970 年代开始，随着科学研究和应用实践的不断深入，人们逐渐意识到，一个理想的垃圾收集器不应在运行时导致应用程序的暂停，不应额外占用大量的内存空间和 CPU 资源，而三种传统的垃圾收集算法都无法满足这些要求。人们必须提出更新的算法或思路，以解决实践中碰到的诸多难题。当时，研究者的努力目标包括： </font></p>
<p><font face=Verdana size=2>第一，提高垃圾收集的效率。使用标记－清除算法的垃圾收集器在工作时要消耗相当多的 CPU 资源。早期的 Lisp 运行环境收集内存垃圾的时间竟占到了系统总运行时间的 40% ！——垃圾收集效率的低下直接造就了 Lisp 语言在执行速度方面的坏名声；直到今天，许多人还条件反射似地误以为所有 Lisp 程序都奇慢无比。 </font></p>
<p><font face=Verdana size=2>第二，减少垃圾收集时的内存占用。这一问题主要出现在复制算法中。尽管复制算法在效率上获得了质的突破，但牺牲一半内存空间的代价仍然是巨大的。在计算机发展的早期，在内存价格以 KB 计算的日子里，浪费客户的一半内存空间简直就是在变相敲诈或拦路打劫。 </font></p>
<p><font face=Verdana size=2>第三，寻找实时的垃圾收集算法。无论执行效率如何，三种传统的垃圾收集算法在执行垃圾收集任务时都必须打断程序的当前工作。这种因垃圾收集而造成的延时是许多程序，特别是执行关键任务的程序没有办法容忍的。如何对传统算法进行改进，以便实现一种在后台悄悄执行，不影响——或至少看上去不影响——当前进程的实时垃圾收集器，这显然是一件更具挑战性的工作。 </font></p>
<p><font face=Verdana size=2>研究者们探寻未知领域的决心和研究工作的进展速度同样令人惊奇：在 1970 年代到 1980 年代的短短十几年中，一大批在实用系统中表现优异的新算法和新思路脱颖而出。正是因为有了这些日趋成熟的垃圾收集算法，今天的我们才能在 Java 或 .NET 提供的运行环境中随心所欲地分配内存块，而不必担心空间释放时的风险。 </font></p>
<div class=section id=S0201><a name=S0201></a>
<h3>&nbsp;</h3>
<h3><font face=Verdana color=#000080 size=3>标记－整理（ Mark-Compact ）算法</font> </h3>
<p><font face=Verdana color=#000080>-------------------------------------------------------------------------------------------------------</font> </p>
<p><font face=Verdana size=2>标记－整理算法是标记－清除算法和复制算法的有机结合。把标记－清除算法在内存占用上的优点和复制算法在执行效率上的特长综合起来，这是所有人都希望看到的结果。不过，两种垃圾收集算法的整合并不像 1 加 1 等于 2 那样简单，我们必须引入一些全新的思路。 1970 年前后， G. L. Steele ， C. J. Cheney 和 D. S. Wise 等研究者陆续找到了正确的方向，标记－整理算法的轮廓也逐渐清晰了起来： </font></p>
<p><font face=Verdana size=2>在我们熟悉的餐厅里，这一次，垃圾收集机器人不再把餐厅分成两个南北区域了。需要执行垃圾收集任务时，机器人先执行标记－清除算法的第一个步骤，为所有使用中的餐巾纸画好标记，然后，机器人命令所有就餐者带上有标记的餐巾纸向餐厅的南面集中，同时把没有标记的废旧餐巾纸扔向餐厅北面。这样一来，机器人只消站在餐厅北面，怀抱垃圾箱，迎接扑面而来的废旧餐巾纸就行了。 </font></p>
<p><font face=Verdana size=2>实验表明，标记－整理算法的总体执行效率高于标记－清除算法，又不像复制算法那样需要牺牲一半的存储空间，这显然是一种非常理想的结果。在许多现代的垃圾收集器中，人们都使用了标记－整理算法或其改进版本。 </font></p>
</div>
<div class=section id=S0202><a name=S0202></a>
<h3>&nbsp;</h3>
<h3><font face=Verdana color=#000080 size=3>增量收集（ Incremental Collecting ）算法</font> </h3>
<p><font face=Verdana color=#000080>-------------------------------------------------------------------------------------------------------</font> </p>
<p><font face=Verdana size=2>对实时垃圾收集算法的研究直接导致了增量收集算法的诞生。 </font></p>
<p><font face=Verdana size=2>最初，人们关于实时垃圾收集的想法是这样的：为了进行实时的垃圾收集，可以设计一个多进程的运行环境，比如用一个进程执行垃圾收集工作，另一个进程执行程序代码。这样一来，垃圾收集工作看上去就仿佛是在后台悄悄完成的，不会打断程序代码的运行。 </font></p>
<p><font face=Verdana size=2>在收集餐巾纸的例子中，这一思路可以被理解为：垃圾收集机器人在人们用餐的同时寻找废弃的餐巾纸并将它们扔到垃圾箱里。这个看似简单的思路会在设计和实现时碰上进程间冲突的难题。比如说，如果垃圾收集进程包括标记和清除两个工作阶段，那么，垃圾收集器在第一阶段中辛辛苦苦标记出的结果很可能被另一个进程中的内存操作代码修改得面目全非，以至于第二阶段的工作没有办法开展。 </font></p>
<p><font face=Verdana size=2>M. L. Minsky 和 D. E. Knuth 对实时垃圾收集过程中的技术难点进行了早期的研究， G. L. Steele 于 1975 年发表了题为&#8220;多进程整理的垃圾收集（ Multiprocessing compactifying garbage collection ）&#8221;的论文，描述了一种被后人称为&#8220; Minsky-Knuth-Steele 算法&#8221;的实时垃圾收集算法。 E. W. Dijkstra ， L. Lamport ， R. R. Fenichel 和 J. C. Yochelson 等人也相继在此领域做出了各自的贡献。 1978 年， H. G. Baker 发表了&#8220;串行计算机上的实时表处理技术（ List Processing in Real Time on a Serial Computer ）&#8221;一文，系统阐述了多进程环境下用于垃圾收集的增量收集算法。 </font></p>
<p><font face=Verdana size=2>增量收集算法的基础仍是传统的标记－清除和复制算法。增量收集算法通过对进程间冲突的妥善处理，允许垃圾收集进程以分阶段的方式完成标记、清理或复制工作。详细分析各种增量收集算法的内部机理是一件相当繁琐的事情，在这里，读者们需要了解的仅仅是： H. G. Baker 等人的努力已经将实时垃圾收集的梦想变成了现实，我们再也不用为垃圾收集打断程序的运行而烦恼了。 </font></p>
</div>
<div class=section id=S0203><a name=S0203></a>
<h3>&nbsp;</h3>
<h3><font face=Verdana color=#000080 size=3>分代收集（ Generational Collecting ）算法</font> </h3>
<p><font face=Verdana color=#000080>-------------------------------------------------------------------------------------------------------</font> </p>
<p><font face=Verdana size=2>和大多数软件开发技术一样，统计学原理总能在技术发展的过程中起到强力催化剂的作用。 1980 年前后，善于在研究中使用统计分析知识的技术人员发现，大多数内存块的生存周期都比较短，垃圾收集器应当把更多的精力放在检查和清理新分配的内存块上。这个发现对于垃圾收集技术的价值可以用餐巾纸的例子概括如下： </font></p>
<p><font face=Verdana size=2>如果垃圾收集机器人足够聪明，事先摸清了餐厅里每个人在用餐时使用餐巾纸的习惯——比如有些人喜欢在用餐前后各用掉一张餐巾纸，有的人喜欢自始至终攥着一张餐巾纸不放，有的人则每打一个喷嚏就用去一张餐巾纸——机器人就可以制定出更完善的餐巾纸回收计划，并总是在人们刚扔掉餐巾纸没多久就把垃圾捡走。这种基于统计学原理的做法当然可以让餐厅的整洁度成倍提高。 </font></p>
<p><font face=Verdana size=2>D. E. Knuth ， T. Knight ， G. Sussman 和 R. Stallman 等人对内存垃圾的分类处理做了最早的研究。 1983 年， H. Lieberman 和 C. Hewitt 发表了题为&#8220;基于对象寿命的一种实时垃圾收集器（ A real-time garbage collector based on the lifetimes of objects ）&#8221;的论文。这篇著名的论文标志着分代收集算法的正式诞生。此后，在 H. G. Baker ， R. L. Hudson ， J. E. B. Moss 等人的共同努力下，分代收集算法逐渐成为了垃圾收集领域里的主流技术。 </font></p>
<p><font face=Verdana size=2>分代收集算法通常将堆中的内存块按寿命分为两类，年老的和年轻的。垃圾收集器使用不同的收集算法或收集策略，分别处理这两类内存块，并特别地把主要工作时间花在处理年轻的内存块上。分代收集算法使垃圾收集器在有限的资源条件下，可以更为有效地工作——这种效率上的提高在今天的 Java 虚拟机中得到了最好的证明。 </font></p>
</div>
</div>
<div class=section id=S03><a name=S03></a>
<h3>&nbsp;</h3>
<h3><font face=Verdana color=#000080 size=3>应用浪潮</font> </h3>
<p><font face=Verdana color=#000080>-------------------------------------------------------------------------------------------------------</font> </p>
<p><font face=Verdana size=2>Lisp 是垃圾收集技术的第一个受益者，但显然不是最后一个。在 Lisp 语言之后，许许多多传统的、现代的、后现代的语言已经把垃圾收集技术拉入了自己的怀抱。随便举几个例子吧：诞生于 1964 年的 Simula 语言， 1969 年的 Smalltalk 语言， 1970 年的 Prolog 语言， 1973 年的 ML 语言， 1975 年的 Scheme 语言， 1983 年的 Modula-3 语言， 1986 年的 Eiffel 语言， 1987 年的 Haskell 语言&#8230;&#8230;它们都先后使用了自动垃圾收集技术。当然，每一种语言使用的垃圾收集算法可能不尽相同，大多数语言和运行环境甚至同时使用了多种垃圾收集算法。但无论怎样，这些实例都说明，垃圾收集技术从诞生的那一天起就不是一种曲高和寡的&#8220;学院派&#8221;技术。 </font></p>
<p><font face=Verdana size=2>对于我们熟悉的 C 和 C++ 语言，垃圾收集技术一样可以发挥巨大的功效。正如我们在学校中就已经知道的那样， C 和 C++ 语言本身并没有提供垃圾收集机制，但这并不妨碍我们在程序中使用具有垃圾收集功能的函数库或类库。例如，早在 1988 年， H. J. Boehm 和 A. J. Demers 就成功地实现了一种使用保守垃圾收集算法（ Conservative GC Algorithmic ）的函数库（参见 </font><a href="http://www.hpl.hp.com/personal/Hans_Boehm/gc"><font face=Verdana size=2>http://www.hpl.hp.com/personal/Hans_Boehm/gc </font></a><font face=Verdana size=2>）。我们可以在 C 语言或 C++ 语言中使用该函数库完成自动垃圾收集功能，必要时，甚至还可以让传统的 C/C++ 代码与使用自动垃圾收集功能的 C/C++ 代码在一个程序里协同工作。 </font></p>
<p><font face=Verdana size=2>1995 年诞生的 Java 语言在一夜之间将垃圾收集技术变成了软件开发领域里最为流行的技术之一。从某种角度说，我们很难分清究竟是 Java 从垃圾收集中受益，还是垃圾收集技术本身借 Java 的普及而扬名。值得注意的是，不同版本的 Java 虚拟机使用的垃圾收集机制并不完全相同， Java 虚拟机其实也经过了一个从简单到复杂的发展过程。在 Java 虚拟机的 1.4.1 版中，人们可以体验到的垃圾收集算法就包括分代收集、复制收集、增量收集、标记－整理、并行复制（ Parallel Copying ）、并行清除（ Parallel Scavenging ）、并发（ Concurrent ）收集等许多种， Java 程序运行速度的不断提升在很大程度上应该归功于垃圾收集技术的发展与完善。 </font></p>
<p><font face=Verdana size=2>尽管历史上已经有许多包含垃圾收集技术的应用平台和操作系统出现，但 Microsoft .NET 却是第一种真正实用化的、包含了垃圾收集机制的通用语言运行环境。事实上， .NET 平台上的所有语言，包括 C# 、 Visual Basic .NET 、 Visual C++ .NET 、 J# 等等，都可以通过几乎完全相同的方式使用 .NET 平台提供的垃圾收集机制。我们似乎可以断言， .NET 是垃圾收集技术在应用领域里的一次重大变革，它使垃圾收集技术从一种单纯的技术变成了应用环境乃至操作系统中的一种内在文化。这种变革对未来软件开发技术的影响力也许要远远超过 .NET 平台本身的商业价值。 </font></p>
</div>
<div class=section id=S04><a name=S04></a>
<h3>&nbsp;</h3>
<h3><font face=Verdana color=#000080 size=3>大势所趋</font> </h3>
<p><font face=Verdana color=#000080>-------------------------------------------------------------------------------------------------------</font> </p>
<p><font face=Verdana size=2>今天，致力于垃圾收集技术研究的人们仍在不懈努力，他们的研究方向包括分布式系统的垃圾收集、复杂事务环境下的垃圾收集、数据库等特定系统的垃圾收集等等。 </font></p>
<p><font face=Verdana size=2>但在程序员中间，仍有不少人对垃圾收集技术不屑一顾，他们宁愿相信自己逐行编写的 free 或 delete 命令，也不愿把垃圾收集的重任交给那些在他们看来既蠢又笨的垃圾收集器。 </font></p>
<p><font face=Verdana size=2>我个人认为，垃圾收集技术的普及是大势所趋，这就像生活会越来越好一样毋庸置疑。今天的程序员也许会因为垃圾收集器要占用一定的 CPU 资源而对其望而却步，但二十多年前的程序员还曾因为高级语言速度太慢而坚持用机器语言写程序呢！在硬件速度日新月异的今天，我们是要吝惜那一点儿时间损耗而踟躇不前，还是该坚定不移地站在代码和运行环境的净化剂——垃圾收集的一边呢？ </font></p>
</div>
<img src ="http://www.cppblog.com/majianan/aggbug/14666.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cppblog.com/majianan/" target="_blank">马嘉楠</a> 2006-11-04 16:19 <a href="http://www.cppblog.com/majianan/archive/2006/11/04/14666.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>2006-10-28 技术网摘</title><link>http://www.cppblog.com/majianan/archive/2006/10/28/14303.html</link><dc:creator>马嘉楠</dc:creator><author>马嘉楠</author><pubDate>Sat, 28 Oct 2006 01:59:00 GMT</pubDate><guid>http://www.cppblog.com/majianan/archive/2006/10/28/14303.html</guid><wfw:comment>http://www.cppblog.com/majianan/comments/14303.html</wfw:comment><comments>http://www.cppblog.com/majianan/archive/2006/10/28/14303.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.cppblog.com/majianan/comments/commentRss/14303.html</wfw:commentRss><trackback:ping>http://www.cppblog.com/majianan/services/trackbacks/14303.html</trackback:ping><description><![CDATA[
		<br />
		<font face="Verdana" color="#0000ff">
				<font color="#000080">
						<strong>1.</strong>
				</font>   </font>
		<a class="" title="" href="http://dev.yesky.com/msdn/332/2628832.shtml" target="">
				<font face="Verdana" color="#0000ff">解析C++/CLI之头文件、内联函数与数组</font>
		</a>
		<br />
		<br />
		<font face="Verdana" color="#0000ff">
				<font color="#000080">
						<strong>2.</strong>
				</font>  </font>
		<a class="" title="" href="http://tech.sina.com.cn/s/2006-10-23/09081197247.shtml" target="">
				<font face="Verdana" color="#0000ff"> 解析C++/CLI之头文件、内联函数与数组(2)</font>
		</a>
		<br />
		<br />
		<font face="Verdana" color="#0000ff">
				<font color="#000080">
						<strong>3.</strong>
				</font>  </font>
		<a class="" title="" href="http://tech.sina.com.cn/s/2006-10-23/09081197248.shtml" target="">
				<font face="Verdana" color="#0000ff"> 解析C++/CLI之头文件、内联函数与数组(3)</font>
		</a>
		<br />
		<br />
		<font face="Verdana" color="#000080">
				<strong>4.</strong>    <a class="" title="" href="http://soft.yesky.com/234/2536734.shtml?324" target="">保卫C++：安全STL编程中的受检迭代子</a><br /><br /><strong>5.</strong>   </font>
<img src ="http://www.cppblog.com/majianan/aggbug/14303.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cppblog.com/majianan/" target="_blank">马嘉楠</a> 2006-10-28 09:59 <a href="http://www.cppblog.com/majianan/archive/2006/10/28/14303.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>极限编程――理解和实践（转载）</title><link>http://www.cppblog.com/majianan/archive/2006/08/26/11728.html</link><dc:creator>马嘉楠</dc:creator><author>马嘉楠</author><pubDate>Sat, 26 Aug 2006 02:15:00 GMT</pubDate><guid>http://www.cppblog.com/majianan/archive/2006/08/26/11728.html</guid><wfw:comment>http://www.cppblog.com/majianan/comments/11728.html</wfw:comment><comments>http://www.cppblog.com/majianan/archive/2006/08/26/11728.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.cppblog.com/majianan/comments/commentRss/11728.html</wfw:commentRss><trackback:ping>http://www.cppblog.com/majianan/services/trackbacks/11728.html</trackback:ping><description><![CDATA[<p><font face=Verdana size=2>作为敏捷软件开发领域主流的开发方法，极限编程与其说是一种系统的方法学，倒更像是一系列最佳实践的有机结合。在这些最佳实践中，有些是已经广为人们所接受的（如编码标准），而更多的则极具颠覆性，初看之下让人似乎难以接受。</font> </p>
<p><font face=Verdana size=2>本文中，我将针对这些看似怪异的最佳实践阐述我的观点，并简述我对实施这些最佳实践的一些思考。</font> </p>
<p><font face=Verdana size=2>一、计划游戏</font> </p>
<p><font face=Verdana size=2>能够把计划叫做&#8220;游戏&#8221;是需要一定勇气的。在传统的软件开发方法学中，计划是举足轻重的一环，在制订计划前需要仔细的估算，在计划实施的过程中，还要不停的跟踪、修正，直至整个项目完成。</font> </p>
<p><font face=Verdana size=2>而在XP中，计划似乎要轻松许多。这并不是因为计划本身变得草率和无关痛痒，而是得益于XP的小版本发布思维。软件的一个版本是如此短小简单，以至于对它进行完整的评估、预算、跟踪和修正要容易许多。事实上，XP中采用的计划方式（业务人员和开发人员共同参与，各司其职）在大多数现代软件企业中早已采用，但是由于项目过于庞大，很难在开始阶段制订出完善的计划。而在XP中，人们只需要针对一个一两个月的小项目进行跟踪和管理，无形中降低了计划的风险。</font> </p>
<p><font face=Verdana size=2>二、隐喻</font> </p>
<p><font face=Verdana size=2>&nbsp;&nbsp;&nbsp; 在XP中，人们经常会使用隐喻来代替传统开发过程中的体系结构设计。从指导开发的角度来说，隐喻似乎不够精确，容易让人误解。但是，对于具有类似背景的同一个项目组中的开发人员来说，隐喻则更便于理解和交流。很难想象两个程序员面对着一张庞大的体系结构图时能够真正有效的沟通，而隐喻很好的解决了这个问题。</font> </p>
<p><font face=Verdana size=2>三、简单设计</font> </p>
<p><font face=Verdana size=2>不知道从什么时候开始，开发人员习惯了为明天而设计。一个开发人员设计了一个复杂的类继承结构，只是为了提高程序的所谓灵活性。没人知道这样值不值，并不是软件的每一个部分都需要扩展。但是，对于传统的软件开发人员来说，这么做又是迫不得已。如果没有预先做好准备，在变化来临时就会措手不及，付出沉重的代价。</font> </p>
<p><font face=Verdana size=2>但是在XP中，小版本发行的方法使得变化并不那么可怕，而重构的广泛采用，使得代码总是可以在需要时变得更加灵活。此外，由于你的代码总是会被别人审查（代码集体所有权和结对编程），因此也可以避免过于追求简单而忽视了重要的细节。</font> </p>
<p><font face=Verdana size=2>四、测试优先</font> </p>
<p><font face=Verdana size=2>没有代码要测试程序有什么用？这是测试优先最容易让人误解的地方。测试优先能够让开发人员更清楚的认识到，程序将会如何被使用。通过对不同的测试用例的思考，开发人员也能够更清晰的认识到程序的功能外延。而更多的其他的开发人员，则通过测试用例就可以获得一份精确的使用手册，在这份使用手册中，描述了作者考虑到的所有输入和输出结果，这样不仅便于人们了解程序，更增加了发现程序错误的机会（缺失的测试用例往往体现出作者忽视的某些使用情况）。</font> </p>
<p><font face=Verdana size=2>五、结对编程</font> </p>
<p><font face=Verdana size=2>两个程序员坐在一起，能够提高开发效率吗？程序员难道不是一群高傲的猫，习惯于离群索居，把头抬得高高吗？</font> </p>
<p><font face=Verdana size=2>事实并非如此。在一个正确的、合理的、能够实现的大目标下，程序员们不仅能够和平共处，更可以相互合作，创造出优秀的、高质量的程序。沟通一直是软件项目管理中的一个重要议题，而结对编程提供了一个十分有效的沟通渠道。此外，结对编程也更容易让新人融入团体。在几个高级程序员的指引下，他会更容易找出程序的脉络，把握程序的思想。较之正规的培训，这种方式更轻松也更有效。对于团队中的所有程序员来说，结对编程都是一个了解其他人设计思想的机会，通过结对编程，能够更好的实现代码集体所有权，也能够降低因为人员流动造成的风险。</font> </p>
<p><font face=Verdana size=2>结对编程最大的好处在于，能够极大的减少程序中潜在变化的可能性。两个人通过交流互相交换自己对程序的不同理解，更容易找出程序中可能出现的变化或错误，从而使程序更加可靠和健壮。</font> </p>
<p><font face=Verdana size=2>六、持续集成</font> </p>
<p><font face=Verdana size=2>集成一直是最费力的工作之一，本来工作的好好的代码，放在一起就不能运转，更糟糕的是成百上千条不知所云的错误码，没有人知道这些错误码来自何处。这是每个项目几乎都会遇到的最困难的阶段，程序员们必须集合在一起，翻阅数量巨大的接口定义文件，反复查看代码，同时还要不断的做出承诺。</font> </p>
<p><font face=Verdana size=2>持续集成正是解决上述问题的方法。通过多次、小增量的集成，我们总是能够以最快的速度定位错误出现的位置（因为增加的代码很少），结合大量测试用例，我们也可以确保每一个集成版本都尽可能的可靠。</font> </p>
<p><font face=Verdana size=2>此外，持续集成几乎可以在任何时间向我们提供一个可以工作的版本，我们可以将这个版本用于内部讨论和测试、客户展示、客户测试、小版本发布等等，这使得我们不需要花费太多的时间对现有的程序修修补补，以生成一个demo。</font> </p>
<p><font face=Verdana size=2>上文简单叙述了XP中常会引起争议的六个最佳实践的优点。下面本文将结合实际谈谈实施XP中需要注意的一些问题。</font> </p>
<p><font face=Verdana size=2>一、适用性问题</font> </p>
<p><font face=Verdana size=2>XP理论在提出时，明确的说明：XP是适用于中小型团队在需求不明确或者迅速变化的情况下进行软件开发的轻量级方法。这就意味着，XP并不适用于所有情况。在准备实施XP前，你也许需要仔细评估项目的具体情况，以决定是否真的需要采用XP。</font> </p>
<p><font face=Verdana size=2>二、最佳实践间的关联</font> </p>
<p><font face=Verdana size=2>XP的一个特点是，它所推崇的最佳实践几乎总是和其它实践关联紧密，在实施一项最佳实践时，如果不同时实施其它实践，往往难以达到最初的目的。因此，在实施XP时，需要仔细研究各项实践间的关联，以确定最佳的实施方案。</font> </p>
<p><font face=Verdana size=2>三、宽松的环境</font> </p>
<p><font face=Verdana size=2>XP是一种追求自然的工作方法。它所倡导的是，程序员们以最自然开发的方式完成他们的工作。对于习惯了传统开发方法严格管理制度的管理人员来说，这往往是很难接受的。于是就出现了，虽然最高决策人决定实施XP，但管理层却无法（或不愿）给开发人员提供宽松的环境。在一个古板僵化的方框里，开发人员不会真正的回复自然，他们会装作正在实践XP，但事实上，他们依然在老路上行走（可以见到很多这样的例子，比如一些虚张声势的测试用例等等）。</font> </p>
<p><font face=Verdana size=2>四、忍受变化</font> </p>
<p><font face=Verdana size=2>XP对于传统软件项目管理思想的冲击，可能会使很多管理人员感到不舒服。也许XP一经实施，就会给项目组带来翻天覆地的变化。如果这样的变化让你感到恐惧，那么请暂时忍耐，你不能肯定这种变化不好，除非你亲眼看到。到那时再决定也不迟。</font> </p>
<p><font face=Verdana size=2>五、慢慢来</font> </p>
<p><font face=Verdana size=2>实施XP的过程不能操之过急。最好的方法是，在部分项目组中先行实施，实施时也不需要同时实施所有实践（但要注意各个实践间的关联问题）。有的时候你会发现，在实施了部分实践后，其它的实践也成为水到渠成的事情。当经过仔细评估，确信XP在项目组中确实有效后，再逐步在企业范围内推广，必要的时候，需要采取自愿的原则，由项目组的成员决定是否需要实施XP。</font> </p>
<p><font face=Verdana size=2>以上即是我在软件工程过程课程中以及平时工作、学习中对XP的一些认识。</font> </p>
<p><br><font face=Verdana size=2>来源：</font> <a href="http://blog.csdn.net/leasun/archive/2006/08/15/1067508.aspx"><font face=Verdana size=2>http://blog.csdn.net/leasun/archive/2006/08/15/1067508.aspx</font> </a></p>
<img src ="http://www.cppblog.com/majianan/aggbug/11728.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cppblog.com/majianan/" target="_blank">马嘉楠</a> 2006-08-26 10:15 <a href="http://www.cppblog.com/majianan/archive/2006/08/26/11728.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item></channel></rss>