什么时候不使用垃圾收集?

时间:2009-03-10 03:00:42

标签: language-agnostic memory-management garbage-collection

不使用垃圾收集的明显情况是实时性很强,内存严重受限,并且想要用指针做点苦恼。有没有其他的,较少讨论的,很好的理由为什么有人更喜欢手动内存管理而不是GC?

11 个答案:

答案 0 :(得分:5)

如果你有完全增量的垃圾收集器,并且每个字节的分配内存有一定的执行时间,那么可以实时使用垃圾收集,所以,疯狂的,它不一定是不使用垃圾收集的理由:)

垃圾收集的一个基本问题是,很难估计和管理内存中工作集的实际大小,因为垃圾收集器只能延迟释放内存。所以,是的,当内存受到限制时,垃圾收集可能不是一个好的选择。

垃圾收集的另一个问题是它有时会干扰释放其他资源,例如文件描述符,窗口句柄等,因为垃圾收集器可能只会延迟释放这些资源,导致资源匮乏。

垃圾收集也会导致缓存垃圾,因为内存不一定以本地方式分配。例如,堆栈分配的内存比堆分配的短期对象更加缓存友好。

最后,垃圾收集当然会消耗CPU时间:)所以如果你可以手动编写内存管理代码,你可以节省垃圾收集器消耗的CPU周期:)

答案 1 :(得分:3)

暂时的精神错乱?

实际上,我所知道的唯一一个你没有涉及的案例就是所谓的“基于生命周期的记忆管理”,它有时会被称为“池”或“竞技场”或“地区”。这个想法是你要分配大量的物品,可能很小,而且它们一下子就会死掉。所以你有一个廉价的分配器,然后你可以在一次免费操作中恢复所有对象。

现在有一些程序分析可以让编译器为你做这个,但如果你正在编写C代码,你可以手工完成。在Dave Hanson的C Interfaces and Implementations中有一个实现示例,它在Fraser和Hanson的lcc compiler中使用,它是用C语言编写的,没有垃圾收集器。

答案 2 :(得分:2)

为具有有限资源的嵌入式设备编程时。例如,iPhone使用引用计数。

或者在计算机上编写极其密集的内容时。想到SETI @ Home和视频游戏。

我会建议你不要管理自己的记忆,除非情况表明它确实是必要的。有人着名曾经说过代码的调试难度和编写的两倍。那么,内存管理首先是很难的。 :)

答案 3 :(得分:2)

不使用垃圾收集进行资源管理的唯一原因是,如果您想使用RAII ala C ++,但由于它纯粹适用于内存,即使这样,使用它也是一个合理的想法。 (注意:仍然可以这样做,因为问题与非确定性的终结有关)。

也就是说,使用垃圾收集可以使用比严格需要的内存更多的内存,因此在严重的内存限制区域,甚至无法用于管理GC例程(和代码)的内存,那么这是一个很好的理由也使用它。

此外,如果您使用的语言默认情况下不包含GC,例如C ++,并且您希望使用RAII,那么这也是一个合理的原因,但对于某些问题它可能非常有用。

最终需要权衡 - 您的要求越专业化,特别是在线程安全RAII方面,实施GC的复杂程度越高,GC可能不会为您的应用程序带来太多的收获。

答案 4 :(得分:1)

嗯......我的教授的理由是让我们(他的学生)的生活更加艰难,并教导我们“真实的东西”。哈哈:)

一般情况下,垃圾收集并不总是针对您的特定应用进行优化,因此如果您是一名优秀的程序员,那么您在内存管理方面肯定会比GC更好。

答案 5 :(得分:1)

  

为什么有人更喜欢手动内存管理而不是GC,是否有其他更少讨论的好理由?

也许最重要的未说出口的问题是VM注入的代码,以使其与GC协调工作。特别是,只要将指针写入堆中,所有生产质量的GC都会默默地产生写屏障。

例如,以下F#程序创建一个10,000个int的数组,然后交换它们:

do
  let xs = Array.init 10000 int
  let timer = System.Diagnostics.Stopwatch.StartNew()
  for i=0 to xs.Length-2 do
    for j=i+1 to xs.Length-1 do
      let t = xs.[i]
      xs.[i] <- xs.[j]
      xs.[j] <- t
  printfn "%f" timer.Elapsed.TotalSeconds

int更改为string并且程序运行速度慢2倍,因为可以直接交换整数,而交换引用类型必须产生两个写入障碍。

人们喜欢在地毯下刷的另一个重要情况是传统GC的病态行为。今天,大多数地方选区都是世代相传的,这意味着它们会分配到一个托儿所,然后将幸存者撤离到老一代(通常是标记扫描)。当代际假设(对象死于年轻和旧对象很少涉及较新的对象)成立时,这很有效,因为托儿所中的大多数对象在被高效扫描时都已死亡。但是对象并不总是很年轻,旧对象有时会指向新对象。

特别是,当一个程序分配一个基于数组的大型可变数据结构(例如散列表,散列集,堆栈,队列或堆)然后用新分配的对象填充它时,表现出代际GC的病态行为。 。这些新对象存活下来,因为它们是从较旧的对象引用的,完全违反了世代假设。因此,使用GC的解决方案通常比此处所需的速度慢3倍。

FWIW,我相信标记区域的GC有可能在未来规避这些问题。在标记区域GC中,旧一代是前托儿所的集合。当线程局部区域填充并且发现其中包含大部分可到达的对象时,整个区域可以在逻辑上迁移到旧代,而不复制任何对象,并且可以分配新的托儿所(或者可以回收非完整的旧托儿所)。

答案 6 :(得分:0)

如果你有大量的物品被释放很少,垃圾收集器就会启动,浪费时间只是为了找出只有几个物体要完成。在极端情况下,这可能会导致巨大的性能损失。

答案 7 :(得分:0)

出于安全原因怎么样?例如,如果你在内存中有一个加密私钥,你可能希望在最短的时间内使用它。

话虽如此,我认为硬件的发展方式,学习多线程编程的艺术可能更值得学习。

答案 8 :(得分:0)

一个可能的答案是,&#34;安全/系统稳定性不是主要要求&#34;。

请记住,对内存进行自由统治的应用程序可能会引起各种安全问题,包括简单地分配和不释放内存(DoS攻击是通过可用内存资源不足将系统降低到静止状态)。例如,这是Java安全模型的核心部分 - 它的GC确保永远不会发生这种情况。

我的观点与Jon Harrop的观点一样,GC会因系统性能而增加开销,原因有多种(在此处的其他答案中注明);它更间接但更安全,并且负责内存管理而远离应用程序开发人员;但是对于网球来说,总会有性能成本。

答案 9 :(得分:0)

垃圾收集与泄漏

我避免垃圾收集的主要原因之一是避免泄漏严重不足的地区的资源泄漏。当安全性和简单性是您的目标时,垃圾收集非常有用,但不能避免泄漏软件。

我们在GC遇到的一个常见情况是,很难避免资源泄漏。

现在这可能会让一些人感到困惑,而且垃圾收集如何与不太理想的团队实践相结合会导致软件泄漏,这似乎很自相矛盾,但软件资源的非平凡管理不在于资源与有限的范围,但持久的徘徊。

复杂资源管理

这种复杂性的一个例子是3D软件中的场景图,其中有数百万行代码和数千个对象和类通过事件相互交互。

在这些情况下,这些持久对象通常存储系统中资源的句柄/引用,也许是存在于持久场景图中的其他对象。在这种情况下,您可以拥有一个中央资源R,就像一个复杂的3D模型,需要数百兆的RAM,可以被场景的许多不同部分和用户界面访问。例如,相机和灯光对象都可以存储对象的参考列表,以便从相机视图和照明系统中排除,其中可以包含这些复杂的3D模型。

在这些情况下,在团队环境中,3个独立的开发人员在代码中的几十个不同位置编写存储R的持久句柄/引用的代码并不常见。当用户删除R时,这些地方的所有应释放句柄/参考。

没有垃圾收集,如果其中一个没有这样做(也许他/她有一个糟糕的一天,是一个经验不足的开发人员,是在一个高压的最后期限紧缩与更宽松的测试和审查标准,无论什么原因),留下悬空的指针/句柄/参考。访问它将使用段错误崩溃应用程序。使用调试器跟踪这样的段错误通常会立即显示它发生的位置和原因。

使用垃圾收集,除了长时间运行软件继续泄漏越来越多的资源之外,不会发生任何明显的事情。因为其中一个地方忘记发布引用,永久延长其生命周期,并继续在有效的,未销毁状态下使用它,软件不仅可以继续增加内存使用量,而且运行时间越长越慢它处理用户不再可见的隐藏对象。

崩溃或不崩溃

在这些类型的情况下,有时候在测试过程中可以立即捕获和处理的这个错误引起的明显和明显的崩溃实际上首选到一个无声且非常难以发现的资源泄漏这可能是一个追踪的噩梦,可能永远不会被修复。

因此,如果您正在开展这样一个项目,在测试过程中立即明显且可纠正的崩溃实际上可能优于泄漏软件,这些软件经常在测试雷达下飞行时会出现这样的错误,垃圾收集,除非它非常小心地结合在一起编码标准和每个团队成员之间的意识,以留意其陷阱(例如需要弱或虚构的参考),实际上可能弊大于利。对我来说,垃圾收集最适合更小,更紧凑的团队和项目,实际上是更高,而不是更低级别的州/资源管理专业知识,或者那些资源泄漏不在附近的地方不好意思崩溃。

从战略开发人员的角度来看,一个明显的,面对面的,显示的错误通常比非常微妙的,隐藏的,'没有人知道发生了什么,但发生了一些不好的事情'有点错误。它经常说明你的调试器之间的区别,告诉你发生了什么,而不是盲目地试图在数百万行代码的大海捞针中找到针。管理大型系统中的资源是最难实现的事情之一,而垃圾收集实际上并没有使这更容易。崩溃或不崩溃,这就是问题,在这些情况下,我们经常会看到没有GC的悬空手柄崩溃或者神秘的资源泄漏。在处理潜在巨大资源的性能关键型应用程序中,此类泄漏通常是不可接受的。

答案 10 :(得分:-1)

当您构建高性能应用程序(如第一人称射击游戏)时,您不希望GC可能会影响应用程序的执行。通过手动管理这些应用程序中的记忆,您可以确定释放资源的正确时间。