Why Java and Python garbage collection methods are different?Python使用引用计数方法来处理对象的生命周期。 因此,不再使用的对象将立即被销毁。 但是,在Java中,GC(垃圾收集器)会销毁在特定时间不再使用的对象。 为什么Java选择这种策略,这有什么好处? 这比Python方法更好吗? 使用引用计数存在缺点。其中最常提到的是循环引用:假设A引用B,B引用C和C引用B.如果A将其引用丢弃到B,则B和C仍将具有引用计数1并且不会被删除与传统的引用计数。 CPython(引用计数不是python本身的一部分,但是它的C实现的一部分)捕获带有单独的垃圾收集例程的循环引用,它定期运行... 另一个缺点:引用计数会使执行速度变慢。每次引用和解除引用对象时,解释器/ VM必须检查计数是否已降至0(如果确实,则取消分配)。垃圾收集不需要这样做。 此外,垃圾收集可以在一个单独的线程中完成(虽然它可能有点棘手)。在具有大量RAM的机器上以及仅缓慢使用内存的进程中,您可能根本不想进行GC操作!就性能而言,引用计数在某种程度上是一个缺点...... 实际上,引用计数和Sun JVM使用的策略都是不同类型的垃圾收集算法。 跟踪死对象有两种广泛的方法:跟踪和引用计数。在跟踪GC时,从"根"开始 - 诸如堆栈引用之类的东西,并跟踪所有可到达(实时)对象。任何无法达到的东西都被视为死亡。在引用计数中,每次修改引用时,所涉及的对象都会更新其计数。引用计数设置为零的任何对象都被视为已死。 基本上所有的GC实现都存在折衷,但跟踪通常适用于高通过(即快速)操作但具有更长的暂停时间(UI或程序可能冻结的较大间隙)。引用计数可以在较小的块中运行,但总体上会较慢。这可能意味着更少的冻结,但总体上表现较差。 另外,引用计数GC需要一个循环检测器来清理循环中的任何对象,这些对象不会被它们的引用计数单独捕获。 Perl 5在其GC实现中没有循环检测器,并且可能泄漏循环的内存。
还进行了研究以获得两全其美(低暂停时间,高吞吐量): 达伦托马斯给出了一个很好的答案。但是,Java和Python方法之间的一个重要区别是,在常见情况下(没有循环引用)引用计数对象会立即清除,而不是在某些不确定的日期。 例如,我可以在CPython中编写草率的,不可移植的代码,例如
并且我打开的文件的文件描述符将立即被清除,因为只要对打开文件的引用消失,文件就会被垃圾收集并释放文件描述符。当然,如果我运行Jython或IronPython或者可能是PyPy,那么垃圾收集器不一定会运行到很久以后;可能我会先用完文件描述符而我的程序会崩溃。 所以你应该编写看起来像的代码
但有时人们喜欢依赖引用计数来永远释放资源,因为它有时可以使你的代码更短。 我会说最好的垃圾收集器是性能最好的垃圾收集器,它目前似乎是Java风格的分代垃圾收集器,它可以在一个单独的线程中运行,并且具有所有这些疯狂的优化,等等。编写代码应该可以忽略不计,理想情况下不存在。 我认为IBM的文章"Java理论与实践:垃圾收集的简史"应该有助于解释您的一些问题。 如果你有足够的内存,垃圾收集比引用计数更快(更有效)。例如,复制gc遍历"实时"对象并将它们复制到新空间,并且可以通过标记整个存储区域在一个步骤中回收所有"死"对象。如果你有足够的内存,这是非常有效的。世代收藏品使用"大多数物品年轻化"的知识;通常只有百分之几的对象必须被复制。 [这也是为什么gc可以比malloc / free更快的原因] 引用计数比垃圾收集更节省空间,因为它在内存无法访问时回收内存。如果要将终结器附加到对象(例如,在File对象无法访问时关闭文件),这很好。即使只有百分之几的内存空闲,引用计数系统也可以工作。但是,必须在每个指针赋值时递增和递减计数器的管理成本花费了大量时间,并且仍然需要某种垃圾收集来回收循环。 因此,权衡是明确的:如果您必须在内存受限的环境中工作,或者您需要精确的终结器,请使用引用计数。如果您有足够的内存并且需要速度,请使用垃圾回收。 Java跟踪GC的一个主要缺点是,它会不时"停止世界"并冻结应用程序相当长的时间来完成一个完整的GC。如果堆很大并且对象树复杂,它将冻结几秒钟。此外,每个完整的GC一遍又一遍地访问整个对象树,这可能是非常低效的。 Java执行GC的方式的另一个缺点是你必须告诉jvm你想要的堆大小(如果默认值不够好); JVM从该值派生出几个阈值,当堆中的垃圾堆叠太多时,这些阈值将触发GC进程。 我认为这实际上是Android(基于Java)的不稳定感觉的主要原因,即使在最昂贵的手机上,与iOS的平滑性(基于ObjectiveC,并使用RC)相比。 我很想看到一个启用RC内存管理的jvm选项,并且可能只保留GC,以便在没有剩余内存时作为最后的手段运行。 在多线程环境中,引用计数特别难以有效地执行。我不知道如果没有进入硬件辅助事务或类似(当前)不寻常的原子指令,你甚至会开始这样做。 引用计数易于实现。 JVM在竞争实施中投入了大量资金,因此对于非常困难的问题实施非常好的解决方案也就不足为奇了。但是,在JVM中定位您喜欢的语言变得越来越容易。 最新的Sun Java VM实际上有多个GC算法,您可以调整它们。故意省略Java VM规范指定实际GC行为以允许针对不同VM的不同(和多个)GC算法。 例如,对于所有不喜欢默认Sun Java VM GC行为的"停止世界"方法的人来说,有一些VM,例如IBM的WebSphere Real Time,它允许在Java上运行实时应用程序。 由于Java VM规范是公开可用的,因此(理论上)没有什么可以阻止任何人实现使用CPython的GC算法的Java VM。 在游戏的后期,但我认为在python中RC的一个重要原理是它的简单性。例如,请参阅Alex Martelli的这封电子邮件。 (我在谷歌缓存外找不到链接,电子邮件日期是2005年10月13日在python列表中)。 |