垃圾收集器如何知道对象和变量超出范围,以便垃圾收集器可以收集它们?
答案 0 :(得分:6)
简而言之:每个应用程序都有一组根。 Roots标识存储位置,这些位置引用托管堆上的对象或设置为null的对象。
当垃圾收集器开始运行时,它会假设堆中的所有对象都是垃圾。
垃圾收集器开始遍历根并构建可从根访问的所有对象的图形。
删除所有无法访问的对象(释放内存)
这取自http://msdn.microsoft.com/en-us/magazine/bb985010.aspx - 关于垃圾收集的好文章。 “有趣”的部分是“垃圾收集算法”。这不是一个很长的部分
答案 1 :(得分:3)
如果不参考Raymond Chen出色的博客文章系列,那么在.NET中没有关于垃圾收集的讨论是完整的:
以下是本系列第一篇文章的引用:
当你问某人垃圾收集是什么时,你得到的答案可能就是“垃圾收集就是当操作环境自动回收程序不再使用的内存时。”它会这样做通过跟踪从根开始的内存来识别哪些对象是可访问的。“
此描述将机制与目标混淆。这就像是说消防员的工作是“驾驶一辆红色卡车并喷水”。这是对消防员做什么的描述,但它忽略了工作的重点(即灭火,更普遍的是消防安全)。
以下是他演示的一些有趣的观点:
正确编写的程序不能假定终结器将会运行。
在执行名为的函数期间,代码块中的对象可以符合集合的条件。
当方法仍在执行时,方法的参数可以符合收集的条件。
一个奇怪的现实世界类比:垃圾收集器可以在潜水员最后一次接触潜水板时收集 - 即使潜水员还在空中!
而且,最简洁:
不要认为GC是追根溯源。将GC视为删除不再使用的内容。
答案 2 :(得分:0)
垃圾收集器(GC)是.NET框架的一部分,该框架由公共语言运行时(CLR)初始化以管理分配和发布应用程序中的内存数量。
垃圾收集器的类型
垃圾收集器可以在多种情况下工作。 CLR提供以下类型的垃圾收集:
并发垃圾收集 并发垃圾回收允许用户线程为第二代垃圾回收的大部分运行。只要托管堆中仍有可用空间供新分配,用户线程就可以运行并创建新对象。
非并发垃圾收集 非并发垃圾收集会在垃圾收集的整个过程中暂停所有非垃圾收集线程,从而在该时间内有效地暂停了应用程序。
背景垃圾收集 后台垃圾回收是并发垃圾回收的替代。它与并发垃圾回收类似,但是允许第0代和第1代垃圾回收中断正在进行的第2代垃圾回收并暂时阻止程序执行。在完成了第0代和第1代垃圾回收之后,继续执行程序以及第2代垃圾回收。这甚至进一步缩短了由于垃圾回收而暂停程序执行的时间。
内存和托管堆 一旦公共语言运行库(CLR)初始化了垃圾收集器,垃圾收集器就会分配一个称为托管堆(与操作系统中的本机堆相对)的内存段,以存储和管理对象。 >
对于每个托管进程,垃圾收集器使用Windows VirtualAlloc 函数在托管堆中保留一部分内存。 (它使用Windows VirtualFree 功能将段释放回操作系统)。进程中的所有线程都为同一堆上的对象分配内存。
要考虑的重要一件事是,每个分配的段的大小都是特定于实现的,并且随时可能更改。
世代
存储在托管堆中的对象根据其年龄进行组织。垃圾收集主要是通过回收通常仅占堆很小一部分的短期对象来进行的。
当垃圾收集器检测到某代的生存率很高时,它将增加该代的分配阈值。
垃圾收集器阶段
该收集是由以下条件之一触发的:
系统的物理内存不足。可以通过 来自操作系统的内存不足通知或内存不足,如 主机。
托管堆上分配的对象使用的内存 超过可接受的阈值。此阈值是连续的 在过程运行时进行调整。
将调用GC.Collect方法。在几乎所有情况下,您都没有 之所以调用此方法,是因为垃圾收集器连续运行。
此时,垃圾收集器传递将引发以下阶段:
标记阶段:GC创建所有活动对象(所有 不在列表上的对象可能会被删除) 使用以下信息确定对象是否为 是否生活:
重定位阶段:所有动态对象列表中所有对象的引用在重定位阶段中进行了更新,以便它们指向对象将要位于的新位置在压实阶段重新定位。 压实阶段:回收死物占据的空间并压实尚存的物体。压缩阶段将在垃圾回收后幸存下来的对象移到该段的较旧端。随着释放死对象占用的空间并移动剩余的活动对象,堆会在压缩阶段被压缩。垃圾回收后剩余的所有活动对象均按其原始顺序移至堆内存的较早一端
资源
官方文档:https://docs.microsoft.com/en-us/dotnet/standard/garbage-collection/fundamentals
答案 3 :(得分:-4)
请浏览http://msdn.microsoft.com/en-us/magazine/bb985010.aspx。 正如它所说的
垃圾收集器检查堆中是否有任何对象不再被应用程序使用。如果存在此类对象,则可以回收这些对象使用的内存。