我一直在想,为什么Java中的垃圾收集器会在任何时候激活,而不是激活:
if(obj.refCount == 0)
{
delete obj;
}
Java有什么我忽略的优点吗?
感谢
每个JVM都不同,但HotSpot JVM并不主要依赖引用计数作为垃圾收集的手段。参考计数具有实现简单的优点,但它本身就容易出错。特别是,如果有一个引用循环(一组在循环中相互引用的对象),则引用计数将无法正确回收这些对象,因为它们的引用计数都为非零。这迫使你不时地使用辅助垃圾收集器,这往往会更慢(Mozilla Firefox就有这个问题,IIRC的解决方案是在引用计数的基础上添加一个垃圾收集器)。这就是为什么,例如,像C++这样的语言倾向于使用引用计数的shared_ptr
和不使用引用循环的weak_ptr
的组合。
此外,将引用计数与每个对象关联会使分配引用的成本比正常情况下更高,因为调整引用计数需要额外的记账(在存在多线程的情况下,这只会变得更糟)。此外,使用引用计数排除了某些类型的快速内存分配器的使用,这可能是一个问题。它还倾向于导致原始形式的堆碎片,因为对象分散在内存中,而不是紧密封装,从而减少分配时间并导致局部性差。
HotSpot JVM使用各种不同的技术来进行垃圾收集,但其主要垃圾收集器称为停止和复制收集器。该收集器通过在内存中相邻地分配对象来工作,并允许以极快的速度(一条或两条汇编指令)分配新对象。当空间耗尽时,所有新对象都会同时被GC’ed,这通常会杀死大多数构建的新对象。因此,GC比典型的引用计数实现快得多,并且最终具有更好的局部性和更好的性能。
为了比较垃圾收集技术,以及快速概述HotSpot中GC的工作原理,您可能想查看这些来自我去年夏天教的编译器课程的讲座幻灯片。您可能还想查看HotSpot垃圾收集白皮书,该白皮书详细介绍了垃圾收集器的工作方式,包括在逐个应用程序的基础上调整收集器的方法。
希望这能有所帮助!
参考计数有以下限制:
- 它对多线程性能非常不利(基本上,对象引用的每个赋值都必须受到保护)
- 不能自动释放循环
因为它不能严格根据引用计数工作。
考虑不再可以从应用程序的"根"访问的循环引用。
例如:
APP
引用了SOME_SCREEN
SOME_SCREEN
引用了SOME_CHILD
SOME_CHILD
引用了SOME_SCREEN
现在,APP
放弃了对SOME_SCREEN
的引用。
在这种情况下,SOME_SCREEN
仍然有对SOME_CHILD
的引用,而SOME_CHILD
仍然有对SOME_SCREEN
的引用——因此,在这种情况中,您的示例不起作用。
现在,其他公司(苹果和ARC,微软和COM,许多其他公司)都有解决方案,并且工作方式与你描述的更相似
使用ARC,您必须用strong
和weak
等关键字注释引用,让ARC知道如何处理这些引用(并避免循环引用)。。。(不要读太多关于ARC的具体例子,因为ARC在编译过程中提前处理这些事情,本身不需要特定的运行时),所以它肯定可以像描述它一样完成,但它与Java的一些功能不兼容。我也相信COM的工作原理与你描述的更相似。。。但同样,这也并非没有开发者的考虑。
事实上,如果没有应用程序开发人员的一些思考(以避免循环引用等),任何"简单"的引用计数方案都是不可行的
因为现代JVM中的垃圾收集器不再跟踪引用计数。该算法用于教导GC如何工作,但它既消耗资源又容易出错(例如循环依赖关系)。
因为java中的垃圾收集器是基于"youg generation"对象的copying collector
,并且"保有权生成"对象的mark and sweep
。
资源来源:http://java.sun.com/docs/hotspot/gc1.4.2/faq.html