简而言之
终结处理不是由垃圾收集器处理的简单问题。参考计数GC易于使用,但是该GC系列通常是不完整的,需要通过显式触发销毁某些对象和结构并对其进行终结来补偿内存泄漏。跟踪垃圾收集器更为有效,但是与仅标识未使用的内存相比,它们使标识要完成和销毁的对象变得更加困难,因此需要更复杂的管理,而这会花费时间和空间,并且成本很高。实施。
介绍
我假设您要问的是为什么垃圾收集语言不能在垃圾收集过程中自动处理销毁/完成,如备注所示:
我发现非常缺乏这些语言将内存视为值得管理的唯一资源。套接字,文件句柄,应用程序状态如何?
我不同意kdbanman给出的答案。尽管所陈述的事实大部分是正确的,尽管偏向于引用计数,但我认为它们不能正确解释问题中所抱怨的情况。
我认为该答案中发展出的术语不是一个大问题,它更容易使事情混淆。确实,如前所述,术语主要是由激活程序的方式决定的,而不是由它们的操作决定的。关键是,在所有情况下,都需要终结一些清理过程不再需要的对象,并释放它一直在使用的任何资源,而内存只是其中之一。理想情况下,当不再使用该对象时,应该通过垃圾收集器自动完成所有操作。实际上,GC可能会丢失或存在缺陷,这可以通过完成和回收程序的显式触发来补偿。
程序显式触发问题是一个问题,因为当仍在使用中的对象被明确终止时,它可能导致难以分析编程错误。
因此,最好是依靠自动垃圾回收来回收资源。但是有两个问题:
最后,经常被遗忘的重要一点是,如果提供了适当的挂钩并且认为GC周期的成本值得,那么GC周期可以由任何原因触发,而不仅仅是内存不足。因此,当缺少任何一种资源以释放一些资源时,完全可以启动GC。
引用计数垃圾收集器
引用计数是一种较弱的垃圾收集技术,无法正确处理循环。确实,在销毁过时的结构和回收其他资源方面确实很弱,仅因为它在回收内存方面很弱。但是,使用引用计数垃圾收集器(GC)可以最轻松地使用终结器,这是因为ref-count GC会在其ref计数降至0时回收结构,此时它的地址及其类型都可以静态地获知或动态地 因此,有可能在应用适当的终结器之后精确地回收内存,并在所有指向的对象上递归调用该过程(可能通过终结过程)。
简而言之,使用Ref Counting GC可以轻松实现终结处理,但是确实由于圆形结构而导致该GC的“不完整性”,其遭受的程度与内存回收的程度完全相同。换句话说,使用引用计数,内存的管理与套接字,文件句柄等其他资源一样差。
确实,Ref Count GC无法回收循环结构(通常)可能被视为内存泄漏。您不能期望所有GC都能避免内存泄漏。它取决于GC算法以及动态可用的类型结构信息(例如,在
保守的GC中)。
追踪垃圾收集器
没有此类泄漏的更强大的GC系列是跟踪系列,该系列从良好识别的根指针开始探索内存的活动部分。在此跟踪过程中未访问的存储器的所有部分(实际上可以以各种方式分解,但我必须简化)是可以回收的存储器的未使用部分1。这些收集器将回收该程序无法再访问的所有内存部分,无论它做什么。它确实回收了圆形结构,并且更高级的GC基于这种范例的某些变体,有时是非常复杂的。在某些情况下,它可以与引用计数结合使用,并弥补其缺点。
一个问题是您的陈述(在问题末尾):
提供自动垃圾收集的语言似乎是支持对象销毁/完成的主要候选方法,因为他们知道当不再使用某个对象时会100%确定。
在技术上不适合跟踪收集器。
100%可以确定的是,不再使用内存的哪些部分。(更确切地说,应该说它们不再可访问,因为如果程序中仍然有指向它们的无用指针,则仍会考虑使用不再根据程序逻辑使用的某些部分。但是,需要进一步的处理和适当的结构才能知道哪些未使用的对象可能已存储在内存的这些现在未使用的部分中。由于该程序不再连接到存储器的这些部分,因此无法从程序的已知信息中确定。
因此,在经过垃圾收集之后,您将剩下一些内存片段,其中包含不再使用的对象,但是先验地没有办法知道这些对象是什么,以便应用正确的终结方法。此外,如果跟踪收集器是标记清除类型,则可能是某些片段可能包含在上一个GC通道中已经完成但由于碎片原因而未使用的对象。但是,可以使用扩展的显式键入来解决。
尽管一个简单的收集器将回收这些内存片段,而事不宜迟,但终结处理需要经过特定的遍历才能探索该未使用的内存,识别其中包含的对象并应用终结处理过程。但是,这种探索需要确定存储在此处的对象的类型,并且还需要确定类型以应用适当的终结处理(如果有)。
因此,这意味着需要花费更多的GC时间(额外的遍历),并可能需要额外的内存开销,以通过各种技术在该遍历中提供正确的类型信息。这些成本可能是巨大的,因为人们通常只想敲定几个对象,而时间和空间开销可能涉及所有对象。
还有一点是,时间和空间开销可能与程序代码的执行有关,而不仅仅是GC的执行。
针对具体问题,我无法给出更准确的答案,因为我不知道您列出的许多语言的具体情况。对于C语言,类型化是一个非常困难的问题,导致了保守的收集器的发展。我的猜测是这也会影响C ++,但我不是C ++专家。汉斯·博姆(Hans Boehm)对保守气相色谱进行了大量研究,似乎证实了这一点。保守GC不能精确地系统回收所有未使用的内存,因为它可能缺少数据的精确类型信息。由于相同的原因,它将无法系统地应用定型程序。
因此,您可以按照某些语言的要求去做。但是它不是免费的。根据语言及其实现的不同,即使您不使用该功能,也可能需要付费。可以考虑使用各种技术和折衷方法来解决这些问题,但这超出了合理大小的答案的范围。
1-这是跟踪收集的抽象表示(包括复制和标记扫掠GC),根据跟踪收集器的类型而有所不同,并且根据复制或标记以及使用扫描。
finalize
/destroy
是谎言?不能保证它会被执行。而且,即使您不知道何时(自动垃圾收集),以及是否仍有必要的上下文(可能已经被收集)也是如此。因此,以其他方式确保状态的一致性是比较安全的,因此可能需要强制程序员这样做。