当您使用具有垃圾收集功能的语言时,您将无法直接访问内存。相反,您可以访问该数据之上的一些抽象。正确抽象的内容之一是数据块在内存中的实际位置,以及指向其他数据块的指针。当垃圾收集器运行时(这种情况偶尔会发生),它会检查您是否仍然保留对其为您分配的每个内存块的引用。如果你不这样做,它将释放该内存。
不同类型的垃圾收集器之间的主要区别在于它们的效率以及它们可以处理的分配方案类型的限制。
最简单的是正确的引用计数。当您创建对对象的引用时,该对象上的内部计数器就会递增,当您遇到该引用或者它不再在范围内时,(以前的)目标对象上的计数器就会递减。当该计数器达到零时,该对象根本不再被引用并且可以被释放。
引用计数垃圾收集器的问题是它们无法处理循环数据。如果对象 A 具有对对象 B 的引用,并且对象 B 又具有对对象 A 的某些(直接或间接)引用,则它们永远无法被释放,即使链中的任何对象都没有在链外引用(因此程序根本无法访问)。
另一方面,标记和清除算法can处理这个(事情。标记和清除算法的工作原理是定期停止程序的执行,将程序分配的每个项目标记为不可访问。然后,该程序会遍历该程序具有的所有变量,并将它们指向的内容标记为可达。如果这些分配中的任何一个包含对程序中其他数据的引用,则该数据同样被标记为可达等。
这是算法的标记部分。在此刻一切程序可以访问的所有内容(无论如何间接)都被标记为可访问,而程序无法访问的所有内容都被标记为不可访问。垃圾收集器现在可以安全地回收与标记为无法访问的对象关联的内存。
标记和清除算法的问题在于它效率不高——必须停止整个程序才能运行它,并且许多对象引用不会改变。
为了改进这一点,可以通过所谓的“分代垃圾收集”来扩展标记和清除算法。在这种模式下,系统中已经进行过一定次数垃圾收集的对象将被提升到老一代,而老一代不会经常被检查。
这提高了效率,因为对象往往会在很年轻的时候就死掉(想象一下循环内更改的字符串,导致生命周期可能有几百个周期)或寿命很长(用于表示应用程序主窗口的对象,或servlet 的数据库连接)。
可以在维基百科上找到更多详细信息。
根据评论添加:
使用标记和清除算法(以及除引用计数之外的任何其他垃圾收集算法),垃圾收集会执行以下操作:not在您的程序的上下文中运行,因为它必须能够访问您的程序无法直接访问的内容。因此,垃圾收集器在堆栈上运行的说法是不正确的。