垃圾收集机制(Garbage Collection)批判——C++

myan 2001-04-20 12:59:00
垃圾收集机制(Garbage Collection)批判

在Java版发表这篇文章,似乎有点把矛头指向Java了。其实不是,GC是所有新一代语言共有的特征,
Python, Eiffel,C#,Roby等无一例外地都使用了GC机制。但既然Java中的GC最为著名,所以天塌
下来自然应该抗着。

这篇短文源于comp.lang.java.programmer跟comp.lang.c++上发生的一场大辩论,支持C++和Java
的两派不同势力展开了新世纪第一场冲突,跟贴发言超过350,两派都有名角压阵。C++阵营的擂主是
Pete Becker,ACM会员,Dinkumware Ltd. 的技术副总监。此君精通C++和Java,开发过两种语言的
核心类库,但是却对C++狂热之极,而对于Java颇不以为然。平时谈到Java的时候还好,一旦有人胆
敢用Java来批判C++,立刻忍不住火爆脾气跳将出来,以坚韧不拔的毅力和大无畏精神与对手周旋,
舌战群儒,哪怕只剩下一个人也要血战到底。这等奇人当真少见!我真奇怪他整天泡在usenet上,
不用工作么?他的老板P.J. Plauger如此宽宏大量?Java阵营主角是一个网名Razzi的兄弟,另外有
Sun公司大名鼎鼎的Peter van der Linden助阵,妙语连珠,寸土必争,加上人多势众,一度占据优势。
C++阵营里大拿虽然很多,但是大多数没有Pete那么多闲工夫,例如Greg Comeau,Comeau公司老板,
每次来个只言片语,实在帮不了Pete多大忙。但是自从C++阵营中冒出一个无名小子,网名Courage(勇气),
发动对Java GC机制的批判,形势为之一变。C++阵营眼下处于全攻之势,Java阵营疲于防守,只能
招架说:“你们没有证据,没有统计资料”,形势很被动。

垃圾收集(GC)不是一直被Java fans用来炫耀,引以为傲的优点么?怎么成了弱点了?我大惑不解,定睛
一看,才觉得此中颇有道理。

首先,Java Swing库存在大量资源泄漏问题,这一点SUN非常清楚,称之为bugs,正在极力修正。但是看来
这里的问题恐怕不仅是库编写者的疏忽,可能根源在于深层的机制,未必能够轻易解决,搞不好要伤筋动骨。
不过这个问题不是那么根本,C++阵营觉得如果抓住对方的弱点攻击,就算是占了上风也没什么说服力。谁
没有缺点呢?于是反其道而行之,猛烈攻击Java阵营觉得最得意的东西,Java的GC机制本身。

首先来想一想,memory leak到底意味着什么。在C++中,new出来的对象没有delete,这就导致了memory
leak。但是C++早就有了克服这一问题的办法——smart pointer。通过使用标准库里设计精致的auto_ptr
以及各种STL容器,还有例如boost库(差不多是个准标准库了)中的四个smart pointers,C++程序员只要
花上一个星期的时间学习最新的资料,就可以拍着胸脯说:“我写的程序没有memory leak!”。

相比之下,Java似乎更优秀,因为从一开始你就不用考虑什么特殊的机制,大胆地往前new,自有GC替你
收拾残局。Java的GC实际上是JVM中的一个独立线程,采用不同的算法策略来收集heap中那些不再有
reference指向的垃圾对象所占用的内存。但是,通常情况下,GC线程的优先级比较低,只有在当前程序
空闲的时候才会被调度,收集垃圾。当然,如果JVM感到内存紧张了,JVM会主动调用GC来收集垃圾,获取
更多的内存。请注意,Java的GC工作的时机是:1. 当前程序不忙,有空闲时间。2. 空闲内存不足。
现在我们考虑一种常见的情况,程序在紧张运行之中,没哟空闲时间给GC来运行,同时机器内存很大,
JVM也没有感到内存不足,结果是什么?对了,GC形同虚设,得不到调用。于是,内存被不断吞噬,而那些
早已经用不着的垃圾对象仍在在宝贵的内存里睡大觉。例如:

class BadGc {

public void job1() {
String garbage = "I am a garbage, and just sleeping in your precious memory, " +
"how do you think you can deal with me? Daydreaming! HAHA!!!";
....
}

public void job2() {...}

...
...

public void job1000() {...}

public static void main(String[] args) {
bgc = new BadGc();
bgc.job1();
bgc.job2();
...
bgc.job1000();
}
}

运行中,虽然garbage对象在离开job1()之后,就再也没有用了。但是因为程序忙,内存还够用,所以GC得
不到调度,garbage始终不会被回收,直到程序运行到bgc.job1000()时还躺在内存里嘲笑你。没辙吧!

好了,我承认这段程序很傻。但是你不要以为这只是理论上的假设,恰恰相反,大多数实用中的Java程序都有
类似的效应。这就是为什么Java程序狂耗内存,而且好像给它多少内存吃都不够。你花上大笔的银子把内存
从128升到256,再升到512,结果是,一旦执行复杂任务,内存还是被轻易填满,而且多出来的这些内存只是
用来装垃圾,GC还是不给面子地千呼万唤不出来。等到你的内存终于心力交瘁,GC才姗姗来迟,收拾残局。而
且GC工作的方式也很不好评价,一种方法是一旦有机会回收内存,就把所有的垃圾都回收。你可以想象,这要
花很长时间(几百M的垃圾啊!),如果你这时侯正在压下开炮的按钮,GC却叫了暂定,好了,你等死吧!另一
种方法,得到机会之后,回收一些内存,让JVM感到内存不那么紧张时就收手。结果呢,内存里始终有大批垃
圾,程序始终在半死不活的荡着。最后,GC可以每隔一段时间就运行一次,每次只回收一部分垃圾,这是现在
大部分JVM的方式,结果是内存也浪费了,还动不动暂停几百毫秒。难啊!

反过来看看C++利用smart pointer达成的效果,一旦某对象不再被引用,系统刻不容缓,立刻回收内存。这
通常发生在关键任务完成后的清理(cleanup)时期,不会影响关键任务的实时性,同时,内存里所有的对象
都是有用的,绝对没有垃圾空占内存。怎么样?传统、朴素的C++是不是更胜一筹?

据统计,目前的Java程序运行期间占用的内存通常为对应C++程序的4-20倍。除了其它的原因,上面所说的是一个
非常主要的因素。我们对memory leak如此愤恨,不就是因为它导致大量的内存垃圾得不到清除吗?如果有了
GC之后,垃圾比以前还来势汹汹,那么GC又有什么好处呢?

当然,C++的smart pointer现在会使用的人不多,所以现在的C++程序普遍存在更严重的memory leak问题。
但是,如果我奶奶跟舒马赫比赛车输掉了,你能够埋怨那辆车子么?

...全文
774 8 打赏 收藏 转发到动态 举报
写回复
用AI写文章
8 条回复
切换为时间正序
请发表友善的回复…
发表回复
liu_feng_fly 2001-04-20
  • 打赏
  • 举报
回复
看来没学java是对了
ed9er 2001-04-20
  • 打赏
  • 举报
回复
gc运行的时机也可以进行改进,至少可以把内存浪费等等问题控制在一定范围内,这也应该是不用考虑delete从而需要付出的一点代价,这不是顽疾
ed9er 2001-04-20
  • 打赏
  • 举报
回复
what a passional guy :)

gc实际上不是避免了memory leak,只不过是java这个platform内的memory leak(memory blast?),jvm替程序员擦屁股了

效率我倒无所谓,机器会更快,内存会更大,alloc的算法可以针对性的优化,等等

但没有明确的destructor让我很讨厌
yoci 2001-04-20
  • 打赏
  • 举报
回复
smart pointer和处理内存泄漏有什么关系?
有谁能讲解一下?
myan 2001-04-20
  • 打赏
  • 举报
回复
我只不过总结了大辩论中C++一方的观点,肯定片面。大家尽可以批评。

另外,利用C++的numeric工具类和POOMA/Blitz等准标准类库,你的数学运算
速度可以做到天下第一——超越Fortran.
babysloth 2001-04-20
  • 打赏
  • 举报
回复
孟哥您可吓了我一跳,这名字我以为是JAVA的来吹嘘GC的,呵呵。我随便说两句了。

想只new不delete,C++也做得到,除了auto_ptr这类的smart pointer,还有就是
Borland的VCL类库。虽然VCL是Object Pascal写的,思路是一样,很多Object有个
Owner,当Owner析构的时候就把所有以它owner的全析构了,成了一个嵌套结构,当
然最顶层的就得您自己动手来delete了。这个熟悉VCL的朋友最清楚了,比如在Form
上放了一个Button,当Form析构时Button一起也升天了,不过有的类就不行。

关于JAVA的垃圾回收机制,除了SUN的广告词,我感觉真是些垃圾。如果这个再做好一
点,再加上template,我都找不到不用JAVA的理由啦,呵呵。关于JAVA这方面,大家
可以看看http://flyland.home.chinaren.com/cpj/CPJ_guide.htm的定量分析。这
位作者还说到:

----众所周知Java的缺憾在于效率低。解释执行的方式使人误认为Java不能用于科学计
算。然而恰恰相反,有了JIT,用Java编写的纯计算的软件可以以C++的效率运行。Java
的慢却在于面向对象的计算。对象的产生、回收,对象成员的引用到方法的调用,都惊人
的消耗着系统资源。以对象的产生为例,每个Java对象都采用动态内存分配,它是Java
除解释执行以外,引起效率低下的另一个主要原因。全面采用动态内存分配会极度降低内
存利用率,同时每次new操作都要运行一次内存匹配算法,消耗CPU时间。因此在JIT普遍
应用,甚至某些Java编译器能直接产生机器代码的今天,Java的优化能力相当有限。
luxes 2001-04-20
  • 打赏
  • 举报
回复
有道理,有见地
vcbear 2001-04-20
  • 打赏
  • 举报
回复

忍不住说一句: 有点道理。
我学Java,我爱C++。

15,440

社区成员

发帖
与我相关
我的任务
社区描述
C/C++ 非技术区
社区管理员
  • 非技术区社区
加入社区
  • 近7日
  • 近30日
  • 至今
社区公告
暂无公告

试试用AI创作助手写篇文章吧