Java 版 (精华区)

发信人: DreamWeaver (猪头小队长·长队小头猪), 信区: Java
标  题: 垃圾收集机制(Garbage Collection)批判
发信站: 哈工大紫丁香 (2002年10月25日14:49:46 星期五), 站内信件


标题   垃圾收集机制(Garbage Collection)批判    myan(原作)  
  
关键字   Garbage collection, Java, C++ 
  

在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,
这就导致了memoryleak。但是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问题。但是,如果我奶奶跟舒马赫比赛车输掉了,你能够埋怨那辆车子么?

--
  ┌――╮
  │┼☆│                       │︳││ │︳               
  ││↑│ㄟ↗╭→╮╭︿╮ ╭┬╮│︳││ │︳╭→╮╭︿╮ ↑  ↑╭→╮ㄟ↗ 
  ││↑│ ↑ │  ││  │ ↑↑↑│︳││ │︳│  ││  │ │  ││  │ ↑  
  │┼╯│ │ ├→┘│  │ ↑↑↑│︳ ☆  │︳├→┘│  │ │  │├→┘ │  
  └――╯ ▲ ╰→╯╰─≈ ↑↑↑ ↖↗  ↖↗  ╰→╯╰─≈  ↖↗ ╰→╯ ▲                                                                       

※ 来源:·哈工大紫丁香 bbs.hit.edu.cn·[FROM: dw.hit.edu.cn]
※ 修改:·DreamWeaver 於 10月25日14:54:54 修改本文·[FROM: dw.hit.edu.cn]
[百宝箱] [返回首页] [上级目录] [根目录] [返回顶部] [刷新] [返回]
Powered by KBS BBS 2.0 (http://dev.kcn.cn)
页面执行时间:3.955毫秒