为什么垃圾收集语言中不允许free()?

时间:2010-05-04 14:45:34

标签: memory-management garbage-collection free

我正在阅读维基百科上的C#条目,并且遇到了:

  

无法显式释放托管内存;相反,它会自动被垃圾收集。

为什么在具有自动内存管理的语言中,手动管理甚至不能允许?我可以看到,在大多数情况下它没有必要,但是如果你的内存紧张并且不想依赖GC是聪明的话,它会不会派上用场?

11 个答案:

答案 0 :(得分:10)

具有自动内存管理功能的语言旨在提供在存在任何手动内存管理时无法提供的大量内存安全保障。

预防的问题包括

  • 加倍free() s
  • 在指向您不拥有的内存的指针上调用free(),导致在其他地方非法访问
  • 在不是分配函数返回值的指针上调用free(),例如获取堆栈中某个对象的地址或数组或其他分配中的某个对象的地址。
  • 取消引用指向已经free() d
  • 的内存的指针

此外,当GC将实时对象移动到合并区域时,自动管理可以带来更好的性能。这改善了引用的局部性,从而提高了缓存性能。

答案 1 :(得分:4)

垃圾收集通过保证内存分配永远不会别名来强制执行内存分配器的类型安全性。也就是说,如果当前正在将一块内存视为类型T,则内存分配器可以保证(使用垃圾回收)当该引用处于活动状态时,它将始终引用T。更具体地说,这意味着内存分配器永远不会将该内存作为不同的类型返回。

现在,如果内存分配器允许手动free()并使用垃圾收集,则必须确保其他人不引用您free()'的内存;换句话说,您传入free()的引用是对该内存的唯一引用。在大多数情况下,对free()进行任意调用会非常昂贵,因此大多数使用垃圾收集的内存分配器都不允许这样做。

这并不是说不可能;如果您可以表达单一参考类型,您可以手动管理它。但在那时,停止使用GC语言或者根本不担心它会更容易。

答案 2 :(得分:1)

调用GC.Collect几乎总是比使用明确的free方法更好。调用free仅对从无处引用的指针/对象引用有意义。这是容易出错的事情,因为您可能会因为错误的指针而调用free

当运行时环境确实引用计数监视时,它知道哪些指针可以安全释放,哪些指针不能,因此让GC决定释放哪个内存可以避免一个丑陋的漏洞类错误。可以考虑使用GCfree的运行时实现,其中为单个内存块显式调用free可能比运行完整的GC.Collect要快得多(但不要期望“手动”释放每个可能的内存块比GC更快。但我认为C#,CLI(以及其他带有Java等垃圾收集器的语言)的设计者已经决定在这里支持稳健性和安全性。

答案 3 :(得分:1)

在允许手动释放对象的系统中,分配例程必须搜索已释放的内存区域列表以找到一些空闲内存。在基于垃圾收集的系统中,任何可立即可用的可用内存都将位于堆的末尾。系统通常更快更容易忽略堆中间未使用的内存区域,而不是尝试分配它们。

答案 4 :(得分:0)

我不能说这是 的答案,但我想到的是,如果你可以free,你可能会意外地加倍free一个指针/参考甚至更糟,免费使用一个。这违背了使用c#/ java / etc等语言的要点。

当然,一个可能的解决方案是让你的free通过引用接受它的参数,并在释放后将其设置为null。但是,如果他们通过这样的r-value会怎么样:free(whatever())。我想你可能有一个r值版本的重载,但甚至不知道c#是否支持这样的事情:-P。

最后,即使这样也不够,因为正如已经指出的那样,你可以对同一个对象进行多次引用。将一个设置为null将无法阻止其他人访问现已解除分配的对象。

答案 5 :(得分:0)

如果您处于“不想依赖GC聪明”的情况,那么很可能您错误地选择了任务框架。在.net中你可以稍微操作GC(http://msdn.microsoft.com/library/system.gc.aspx),用Java来确定。

我认为你不能免费打电话,因为你开始做GC的一项任务。 GC的效率可以以某种方式得到保证,当它以最佳方式执行时,它会在决定时执行。如果开发人员会干扰GC,可能会降低其整体效率。

答案 6 :(得分:0)

有趣的是,您可以通过System.GC访问垃圾收集器 - 虽然从我读过的所有内容中,强烈建议您允许GC自行管理。

我被建议一次使用第三方供应商的以下2行来处理DLL或COM对象的垃圾收集问题或其他问题:

// Force garbage collection (cleanup event objects from previous run.)
GC.Collect();         // Force an immediate garbage collection of all generations
GC.GetTotalMemory(true);

那就是说,除非我确切知道幕后发生了什么,否则我不会打扰System.GC。在这种情况下,第三方供应商的建议“修复”了我正在处理的有关其代码的问题。但我不禁想知道这是否真的是他们破坏代码的解决方法......

答案 7 :(得分:0)

许多其他答案提供了很好的解释,说明GC在如何针对提供GC的运行时系统进行编程时应该如何工作以及如何思考。

我想添加一个技巧,我尝试用GC语言编程时要记住。规则是“尽快丢弃指针很重要。”通过删除指针我的意思是我不再指向我不再使用的对象。例如,通过将变量设置为Null,可以在某些语言中完成此操作。这可以看作是对垃圾收集器的暗示,只要没有其它指针就可以收集这个对象。

答案 8 :(得分:0)

为什么要使用free()?假设您要释放大量内存。

一种方法是调用垃圾收集器,或者在系统需要时运行它。在这种情况下,如果无法访问大块内存,则将取消分配。 (现代垃圾收集器非常聪明。)这意味着,如果它没有被解除分配,它仍然可以被访问。

因此,如果您可以使用free()而不是垃圾收集器来删除它,那么仍然可以访问该块(如果语言具有该概念,则不能通过弱指针),这意味着您'留下语言等同于悬空指针。

语言可以防御双重释放或试图释放未分配的内存,但它可以避免悬空指针的唯一方法是废除free()或修改其含义,使其不再有用。 / p>

答案 9 :(得分:0)

  

为什么在具有自动内存管理的语言中,甚至不允许手动管理?我可以看到,在大多数情况下,它没有必要,但是如果你的内存紧张并且不想依赖GC很聪明,它会不会派上用场?

在绝大多数垃圾收集语言和VM中,提供free功能没有意义,尽管如果您愿意,您几乎总能使用FFI在托管VM外部分配和释放非托管内存。

垃圾收集语言中缺少free有两个主要原因:

  1. 记忆安全。
  2. 没有指点。
  3. 关于内存安全性,自动内存管理的主要动机之一是消除由不正确的手动内存管理引起的错误类别。例如,手动内存管理使用相同的指针调用free两次或使用不正确的指针可能会破坏内存管理器自己的数据结构并导致程序中稍后的非确定性崩溃(当内存管理器下一次到达其已损坏时)数据)。自动内存管理不会发生这种情况,但暴露free会再次打开这种蠕虫病毒。

    关于指针,free函数在指针指定的位置释放一块已分配的内存,返回内存管理器。垃圾收集语言和VM用更抽象的概念(称为引用)替换指针。大多数生产GC都在移动,这意味着高级代码包含对值或对象的引用,但内存中的基础位置可能会发生变化,因为VM无需高级语言知道即可移动已分配的内存块。这用于压缩堆,防止碎片和改善局部性。

    因此,当您拥有GC时,有充分的理由不要free

答案 10 :(得分:-1)

允许手动管理。例如,在Ruby调用GC.start中将释放所有可以释放的东西,尽管你不能单独释放东西。