当前位置: 首页 > 图灵资讯 > 技术篇> 垃圾收集机制(Garbage Collection)批判

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

来源:图灵教育
时间:2024-02-22 14:36:44

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

在Java版发表这篇文章,似乎把矛头指向了Java。事实并非如此。GC是所有新一代语言的共同特征,Python, Eiffel,C#,Roby等无一例外地都采用了GC机制。但由于Java中的GC最为出名,天塌下来自然应该抵抗。

这篇短文源于compp.lang.java.programercomp.lang.c++在最后一场大辩论中,支持C++和Java的两支不同势力发动了新世纪的第一场冲突,发表了350多次演讲,两支球队都有名角。C++阵营的挑战者是Peteete Becker,ACM会员,Dinkumware Ltd. 技术副总监。这位精通C++和Java的绅士开发了两种语言的核心类库,但他对C++非常热情,对Java并不这么认为。说到Java,没关系。一旦有人敢用Java批评C++,他就会忍不住发脾气,以毅力和无畏精神与对手打交道。即使只剩下一个人,他也会血战到底。这种奇人真的很少见!我真的很奇怪他整天泡在usenet上,不用工作吗?他的老板P.J. Plauger这么宽宏大量?Java阵营的主角是网名Razi的兄弟,还有Sun公司著名的Peterr van der Linden助阵,妙语连珠,寸土必争,再加上人多势众,一度占据优势。C++虽然阵营里有很多大拿,但大部分都没有Pete那么多闲暇时间,比如Greg Comeau,Comeau公司的老板,每次来一句话,真的帮不了Pete多少忙。但由于C++阵营中出现了一个无名小子,网名Courage(勇气),发动了Java 对GC机制的批判,形势变了。C++阵营目前处于全攻状态,Java阵营厌倦了防守,只能招架说:“你们没有证据,没有统计数据”,形势非常被动。

垃圾收集(GC)Java并不总是存在 fans用来炫耀,引以为傲的优点吗?怎么会成为弱点?我很困惑,定睛一看,才觉得挺有道理的。

首先,Java Swing库存中存在大量资源泄漏问题,SUN非常清楚,称之为bugs,正在努力纠正。然而,这里的问题似乎不仅仅是图书馆作家的疏忽。根本原因可能在于深层机制,可能无法轻易解决。也许它会伤害你的肌肉和骨骼。但这个问题并没有那么根本,C++阵营认为,如果他们抓住了对方的弱点,即使他们占了上风,也没有说服力。谁没有缺点?于是反其道而行之,猛烈攻击Java阵营,JavaGC机制本身就是最令人自豪的东西。

首先想一想,memory leak到底意味着什么?在C++中,new出来的对象没有delete,导致memoryleak。但是C++早就有办法克服这个问题了。——smart pointer。通过使用标准库设计精美的auto_ptr和各种STL容器,以及bost库中的四个smart(几乎是标准库) pointers,C++只要程序员花一周时间学习最新信息,他就可以拍拍胸脯说:“我写的程序没有memory leak!”。

相比之下,Java似乎更好,因为你不必从一开始就考虑任何特殊的机制,大胆地向前推进,自己的GC为你收拾残局。Java的GC实际上是JVM中的一个独立线程,采用不同的算法策略收集heap中没有reference指向的垃圾对象占用的内存。然而,GC线程的优先级通常相对较低,只有在当前程序的空闲时间才会被调度和收集垃圾。当然,如果JVM感到内存紧张,JVM会主动调用GC收集垃圾,获得更多内存。请注意,JavaGC的工作时间是: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 一旦某个对象不再被引用,系统就迫切需要立即回收内存。这通常发生在关键任务完成后的清理(cleanup)在这个时期,它不会影响关键任务的实时性。同时,内存中的所有对象都是有用的,绝对没有垃圾占用内存。那怎么样?传统和简单的C++更好吗?

据统计,目前Java程序在运行过程中占用的内存通常是C++程序的4-20倍。除了其他原因,上面提到的是一个非常重要的因素。我们很抱歉 leak之所以如此愤恨,不是因为它导致大量内存垃圾无法清除吗?如果有GC,垃圾比以前更咄逼人,GC有什么好处?

当然,C++smart pointer现在用的人不多,所以现在C++程序中有更严重的memory。 leak问题。但是,如果我奶奶在舒马赫的比赛中输了,你能责怪那辆车吗?