什么时候你可能不想使用垃圾收集?

时间:2009-01-02 19:27:48

标签: memory-management garbage-collection

自LISP早期以来,垃圾收集已经存在,现在 - 几十年后 - 大多数现代编程语言都在使用垃圾收集。

假设您正在使用其中一种语言,那么使用垃圾收集的原因是什么,而是以某种方式手动管理内存分配?

你有没有必要这样做?

如果可能,请举例说明。

14 个答案:

答案 0 :(得分:16)

我能想到一些:

确定性释放/清理

实时系统

不放弃一半的内存或处理器时间 - 取决于算法

更快的内存alloc / dealloc和特定于应用程序的分配,释放和内存管理。基本上编写自己的内存 - 通常用于性能敏感的应用程序。这可以在很好地理解应用程序的行为的情况下完成。对于通用GC(如Java和C#),这是不可能的。

修改

尽管如此,GC确实对社区的大多数人都有好处。它允许我们更多地关注问题领域而不是漂亮的编程技巧或模式。我仍然是一个“非托管”的C ++开发人员。在这种情况下,良好做法和工具会有所帮助。

答案 1 :(得分:6)

内存分配?不,我认为GC比我更好。

但是稀缺的资源分配,如文件句柄,数据库连接等?当我完成时,我编写代码来关闭它们。 GC不会为你做那件事。

答案 2 :(得分:5)

我做了很多嵌入式开发,其中的问题更可能是使用malloc还是静态分配,垃圾收集不是一种选择。

我还编写了许多基于PC的支持工具,并且很乐意在可用的GC中使用GC。足够快,这意味着我不必使用pedant :: std :: string。

我写了很多压缩&加密代码和GC性能通常不够好,除非我真的弯曲实现。 GC还要求您对地址别名技巧要非常小心。我通常在C中编写性能敏感的代码,并从Python / C#前端调用它。

所以我的答案是有理由避免GC,但原因几乎总是性能,然后最好用另一种语言编写需要它的东西,而不是试图欺骗GC。

如果我在MSVC ++中开发一些东西,我从不使用垃圾收集。部分是因为它是非标准的,但也因为我在C ++中没有GC而成长,并在安全的内存回收中自动设计。话虽如此,我认为C ++是一种令人厌恶的东西,它无法提供C语言的翻译透明度和可预测性,或者后来的OO语言的范围内存安全性(以及其他内容)。

答案 3 :(得分:4)

使用垃圾收集器编写实时应用程序可能很困难。也许使用在另一个线程中工作的增量GC,但这是一个额外的开销。

答案 4 :(得分:3)

我能想到的一个案例是当你处理的数据集数量达到数兆字节或更多时。根据情况,您可能希望在完成后立即释放此内存,以便其他应用程序可以使用它。

此外,在处理某些非托管代码时,您可能希望阻止GC收集某些数据,因为它仍然被非托管部分使用。虽然我仍然需要考虑一个很好的理由,为什么简单地保留它的参考可能不够好。 :P

答案 5 :(得分:3)

我所处理的一种情况是图像处理。在研究裁剪图像的算法时,我发现托管库的速度不够快,无法在大图像上或一次在多个图像上剪切。

以合理的速度对图像进行处理的唯一方法是在我的情况下使用非托管代码。这是在C#.NET中开展一个小型的个人侧面项目时,由于项目的规模,我不想学习第三方库,因为我想学习它以改善自己。可能有一个现有的第三方库(也许是Paint.NET)可以做到这一点,但它仍然需要非托管代码。

答案 6 :(得分:3)

两个字:空间强化

我知道这是一个极端的案例,但仍然适用。应用于火星探测器核心的编码标准之一实际上禁止动态内存分配。虽然这确实是极端的,但它说明了“无忧无虑地部署和忘记”理想。

简而言之,要了解您的代码实际上对某人的计算机所做的事情。如果你这样做,而且你是保守的......那么让记忆仙女来处理剩下的事情。当您使用四核开发时,您的用户可能会使用更旧的内存,而且内存更少。

使用垃圾收集作为安全网,请注意您分配的内容。

答案 7 :(得分:1)

理论上,没什么。但在实践中,如果无法为您的应用执行,请不要使用它。

对于不同类型的应用程序,不同的GC算法可能有效,也可能无效。有些GC对于长时间运行的应用程序更好,有些可以调整吞吐量,有些可以减少延迟,有些只是很糟糕。

我有一些实例,其中java的GC不那么高效,我希望我可以管理自己的内存。   基本上我使用的是 TON 的内存,它立刻就变成了垃圾,并且由于GC的工作方式,其中一些最终会在“终身”代中结束,当它不需要时,而且我不能强迫java为所有的内存使用copy-collection。

有16个演出的ram而不是8个可能也解决了这个问题。总而言之,我只需要做一些额外的调整就可以使它工作,而且由于我无法在java中关闭'gc',这是我唯一的选择。

我怀疑Java 7的新GC会解决我的问题。

答案 8 :(得分:1)

有两种主要类型的实时系统,硬和软。主要的区别在于硬实时系统要求算法总是在特定的时间预算中完成,而软系统通常希望它通常发生。软系统可能使用设计良好的垃圾收集器,尽管正常的垃圾收集器是不可接受的。然而,如果硬实时系统算法没有及时完成,那么生命可能处于危险之中。您将在核反应堆,飞机和航天飞机中找到这样的系统,甚至只能在操作系统和驱动器组成的专业软件中找到。我只想说这不是你常见的编程工作。

编写这些系统的人不倾向于使用通用编程语言。 Ada的目的是为了编写这些类型的实时系统。尽管在某些系统中这种系统是一种特殊语言,但语言被进一步缩减为称为Spark的子集。 Spark是Ada语言的一个特殊安全关键子集,它不允许的一个功能是创建一个新对象。对象的新关键字完全被禁止,因为它可能耗尽内存及其可变执行时间。实际上,Spark中的所有内存访问都是使用绝对内存位置或堆栈变量完成的,并且堆上没有新的分配。垃圾收集器不仅完全没用,而且对保证执行时间有害。

这些类型的系统并不常见,但如果存在这些系统,则需要一些非常特殊的编程技术,并确保执行时间至关重要。

答案 9 :(得分:1)

几乎所有这些答案都归结为性能和控制。我在之前的帖子中没有看到的一个角度是,跳过GC会以两种方式为您的应用程序提供更可预测的缓存行为。

  1. 在某些缓存敏感的应用程序中,让语言每隔一段时间自动删除缓存(尽管这取决于实现)可能是个问题。
  2. 虽然GC与分配正交,但大多数实现都会减少对细节的控制。许多高性能代码具有针对缓存调整的数据结构,并且实现诸如cache-oblivious algorithms之类的东西需要对内存布局进行更细粒度的控制。虽然概念上没有理由GC与手动指定内存布局不兼容,但我想不出一个可以让你这么做的流行实现。

答案 10 :(得分:1)

  

假设你正在使用其中一种语言,你有什么理由不使用垃圾收集,而是以某种方式手动管理内存分配?

可能有几种可能的原因:

  1. 由于垃圾收集器造成的程序延迟高得令人无法接受。

  2. 回收前的延迟时间长得令人无法接受,例如:在.NET上分配一个大数组会把它放在很少收集的大对象堆(LOH)中,因此它会在无法访问后暂停一段时间。

  3. 与垃圾收集相关的其他开销高得令人无法接受,例如:写屏障。

  4. 垃圾收集器的特性是不可接受的,例如当32位地址空间耗尽时,即使理论上有足够的可用空间,.NET上的加倍数组也会导致大对象堆(LOH)导致内存不足。在OCaml(可能是大多数GC语言)中,具有深度线程堆栈的函数渐近运行速度较慢。同样在OCaml中,通过GC上的全局锁定防止线程并行运行,因此(理论上)可以通过删除到C并使用手动内存管理来实现并行性。

  5.   

    你有没有必要这样做?

    不,我从未拥有来做到这一点。我做得很开心。例如,我用F#(.NET语言)编写了一个垃圾收集器,为了使我的时间具有代表性,我采用了无分配样式以避免GC延迟。在生产代码中,我不得不使用垃圾收集器如何工作的知识来优化我的程序,但我甚至从来没有在.NET中绕过它,更不用说完全丢弃.NET了,因为它强加了GC。

    最接近丢弃垃圾收集的是丢弃OCaml语言本身,因为它的GC阻碍了并行性。但是,我最终迁移到了F#这是一种.NET语言,因此继承了CLR优秀的多核GC。

答案 11 :(得分:0)

我不太明白这个问题。由于您询问使用GC的语言,我假设您要求的示例如

  1. 即使我知道它已经死了,也可以故意依赖引用,也许重用该对象以满足未来的分配请求。
  2. 跟踪一些对象并明确地将它们关闭,因为它们拥有无法使用垃圾收集器轻松管理的资源(打开文件描述符,屏幕上的窗口,等等)。
  3. 我从来没有找到做#1的理由,但#2偶尔会出现。许多垃圾收集器提供 finalization 的机制,这是一个绑定到对象的操作,系统在回收对象之前运行该操作。但是系统通常会提供 no 保证是否或者终结器是否实际运行,因此最终确定的效用可能有限。

    我用垃圾收集语言做的主要事情是密切关注每单位其他工作的分配数量。分配通常是性能瓶颈,尤其是在Java或.NET系统中。它在ML,Haskell或LISP等语言中不是一个问题,它们的设计通常都是为了让程序像疯了一样分配。


    编辑:对评论的回复时间更长。

    并非每个人都明白,在性能方面,分配器和GC必须被视为一个团队。在最先进的系统中,分配是从连续的自由空间(“托儿所”)完成的,并且与测试和增量一样快。但除非分配的对象非常短暂,否则该对象会产生债务:它必须被复制出苗圃,如果它存在一段时间,它可能会被复制几次。最好的系统使用连续的自由空间进行分配,并在某些时候从复制切换到标记/扫描或标记/扫描/压缩旧对象。因此,如果你非常挑剔,如果

    ,你可以放弃忽略分配
    • 您知道您正在处理从连续自由空间(托儿所)分配的最先进系统。
    • 您分配的对象非常短暂(在托儿所中少于一个分配周期)。

    否则,分配的对象最初可能很便宜,但它们代表了必须在以后完成的工作。即使分配本身的成本是测试和增量,减少分配是提高性能的最佳方法。我使用最先进的分配器和收集器调整了几十个ML程序,这仍然是 true; 即使使用最好的技术,内存管理也是一个常见的性能瓶颈

    令人惊讶的是,即使是非常短暂的对象,有多少分配器处理不好。我只是从Lua 5.1.4(可能是最快的脚本语言,使用世代GC)获得了一个很大的加速,通过替换30个替换的序列,每个替换分配一个大表达式的新副本,同时替换30个名​​称,分配了一个大表达式的副本而不是30个。性能问题消失了。

答案 12 :(得分:0)

在视频游戏中,您不希望在游戏帧之间运行垃圾收集器。

  

例如,Big Bad就在眼前   你和你的生活是十分之一。   你决定奔向Quad   伤害加电。一旦你拿起来   通电,你准备自己   转向你的敌人开火   你最强大的武器。

     

当通电消失时,它就会消失   运行垃圾是个坏主意   收藏家只是因为游戏世界   必须删除的数据   上电。

视频游戏通常通过弄清楚某个地图中需要什么来管理它们的对象(这就是为什么需要一段时间来加载具有大量对象的地图)。某些游戏引擎会在某些事件发生后调用垃圾收集器(保存后,当引擎检测到附近没有威胁时等)。

除了视频游戏,我没有找到任何理由关闭垃圾收集。

编辑:在阅读完其他评论后,我意识到嵌入式系统和Space Hardening(分别是Bill和Tinkertim的评论)也是关闭垃圾收集器的好理由

答案 13 :(得分:0)

  

执行越关键,你想要推迟垃圾收集的次数就越多,但是推迟垃圾收集的时间越长,它最终会出现的问题就越多。

使用上下文确定需求:

1

  • 垃圾收集应该可以防止内存泄漏
  • 你需要更多的状态而不是你脑子里的管理吗?

2

  • 通过销毁没有引用的对象来返回内存可能是不可预测的
  • 你需要的指针多于你能掌握的指针吗?

3

  • 垃圾收集可能导致资源不足
  • 你有更多的CPU和内存,而不是你能想到的吗?

4

  • 垃圾收集无法处理文件和套接字
  • 您是否主要关注I / O?
  

在使用垃圾收集的系统中,弱指针有时用于实现简单的缓存机制,因为只有当内存压力触发垃圾收集时才会释放没有强引用的对象。但是,使用ARC时,一旦删除了最后一个强引用,就会释放值,使得弱引用不适用于此目的。

<强>参考