【问题标题】:Garbage Collector in .NET.NET 中的垃圾收集器
【发布时间】:2012-04-04 05:55:02
【问题描述】:

垃圾收集器如何知道对象和变量超出范围,以便它们可以被垃圾收集器收集?

【问题讨论】:

标签: asp.net .net c#-4.0 garbage-collection


【解决方案1】:

简而言之:每个应用程序都有一组根。根标识存储位置,这些位置引用托管堆上的对象或设置为 null 的对象。

当垃圾收集器开始运行时,它会假设堆中的所有对象都是垃圾。

垃圾收集器开始遍历根并构建从根可到达的所有对象的图。

所有不可达的对象都被删除(内存被释放)

这取自http://msdn.microsoft.com/en-us/magazine/bb985010.aspx - 关于垃圾收集的好文章。对你来说“有趣”的部分是“垃圾收集算法”。这不是一个很长的部分

【讨论】:

    【解决方案2】:

    如果不参考 Raymond Chen 的一系列优秀博文,对 .NET 中的垃圾收集的讨论是不完整的:

    这是该系列第一篇文章的引述:

    当你问别人什么是垃圾回收时,你得到的答案可能类似于“垃圾回收是指操作环境自动回收程序不再使用的内存。它会这样做通过从根开始跟踪内存来识别哪些对象是可访问的。”

    这个描述混淆了机制和目标。就像说消防员的工作是“开红色卡车喷水”一样。这是对消防员工作的描述,但它忽略了工作的重点(即扑灭火灾,更一般地说,消防安全)。

    以下是他展示的一些有趣的观点:

    一个正确编写的程序不能假设终结器会运行。

     

    代码块中的对象可以在其调用的函数执行期间有资格被收集

     

    方法的参数可以在方法仍在执行时被收集

     

    一个奇怪的现实类比:垃圾收集器可以在潜水员最后一次触摸你的跳水板时收集它——即使潜水员还在空中!

    最简洁的:

    不要将 GC 视为跟踪根。将 GC 视为删除您不再使用的东西。

    【讨论】:

      【解决方案3】:

      垃圾收集器 (GC) 是 .NET 框架的一部分,由 公共语言运行时 (CLR) 初始化以管理分配和释放应用程序中的内存。

      垃圾收集器的类型

      垃圾收集器可以在多种场景中工作。 CLR 提供以下类型的垃圾回收:

      • 工作站垃圾回收 (GC) 以正常优先级运行 线程,可以是并发的或非并发的,设计用于 客户端应用
      • 服务器垃圾收集专为服务器应用程序和 创建一个单独的托管堆和相应的垃圾 每个逻辑 CPU 的收集线程。线程运行在最高 优先使其更快但更占用资源。在 .NET Core 中, .NET Framework 4.5 及更高版本的服务器垃圾回收可以 非并发或后台。在 .NET Framework 4 及更早版本中 版本,服务器垃圾收集是非并发的。

      并发垃圾回收 并发垃圾收集允许用户线程在第 2 代垃圾收集的大部分时间运行。只要托管堆中仍有用于新分配的空闲空间,就允许用户线程运行并创建新对象。

      非并发垃圾回收 非并发垃圾回收会在整个垃圾回收期间暂停所有非垃圾回收线程,从而在这段时间内有效地暂停应用程序。

      后台垃圾回收 后台垃圾回收是并发垃圾回收的替代品。它类似于并发垃圾收集,但允许第 0 代和第 1 代垃圾收集中断正在进行的第 2 代垃圾收集并暂时阻止程序执行。在第 0 代和第 1 代垃圾收集完成后,程序执行和第 2 代垃圾收集都将继续进行。这甚至进一步缩短了程序执行因垃圾回收而暂停的时间。

      内存和托管堆 一旦垃圾收集器被公共语言运行时 (CLR) 初始化,它就会分配一段称为托管堆的内存段,该内存段与操作系统中的本地堆相反,用于存储和管理对象。 p>

      对于每个托管进程,垃圾收集器使用 Windows VirtualAlloc 函数在托管堆中保留一段内存。 (它使用 Windows VirtualFree 功能将段释放回操作系统)。进程中的所有线程都为同一个堆上的对象分配内存。

      需要考虑的重要一点是,每个分配段的大小是特定于实现的,并且随时可能发生变化。

      世代

      存储在托管堆中的对象根据它们的年龄被组织成几代。垃圾回收主要发生在回收短寿命对象时,这些对象通常只占用堆的一小部分。

      • 第 0 代:具有短期对象的最年轻一代(例如。 临时变量或新分配的对象,除非它们很大 对象,在这种情况下,它们会在大对象堆中 第 2 代系列)
      • 第 1 代:如果某些第 0 代对象占用的空间是 没有在垃圾收集运行中释放,那么这些对象得到 移至第 1 代并包含短期对象。
      • 第 2 代。如果某些第 1 代对象占用的空间是 在下一次垃圾回收运行中没有释放,那么这些对象 移到第 2 代。这一代包含长寿 对象(即服务器应用程序中包含静态的对象 在整个过程中有效的数据)。

      当垃圾收集器检测到某一代的存活率很高时,它会增加该代的分配阈值。

      垃圾收集器阶段

      集合由以下条件之一触发:

      • 系统的物理内存不足。这由任一检测到 来自操作系统的内存不足通知或内存不足,如 主持人。

      • 托管堆上分配的对象使用的内存 超过可接受的阈值。这个阈值是连续的 在进程运行时进行调整。

      • 调用 GC.Collect 方法。在几乎所有情况下,您都没有 调用这个方法,因为垃圾收集器一直在运行。

      此时垃圾收集器通过抛出以下阶段:

      1. 标记阶段:GC 创建所有活动对象的列表(所有 不在列表中的对象可能会被删除)by
        使用以下信息来确定对象是否是 生活与否:

        • 堆栈根。 即时 (JIT) 编译器和堆栈遍历器提供的堆栈变量。 JIT 优化可以延长或缩短向垃圾收集器报告堆栈变量的代码区域。
        • 垃圾收集句柄。指向托管对象并且可由用户代码或公共语言运行时分配的句柄。
        • 静态数据。应用程序域中可能引用其他对象的静态对象。每个应用程序域都会跟踪其静态对象。
      2. 重定位阶段:所有活动对象列表中的所有对象的引用都会在重定位阶段更新,以便它们指向对象将要存放的新位置在压实阶段被重新定位。 压缩阶段:回收死对象占用的空间并压缩幸存对象。压缩阶段将在垃圾收集中幸存下来的对象移向段的较旧端。堆在压缩阶段被压缩,因为死对象占用的空间被释放并且剩余的活动对象被移动。垃圾回收后剩余的所有活动对象都按其原始顺序移向堆内存的旧端

      资源

      官方文档https://docs.microsoft.com/en-us/dotnet/standard/garbage-collection/fundamentals

      【讨论】:

        【解决方案4】:

        请转至http://msdn.microsoft.com/en-us/magazine/bb985010.aspx。 正如它所说的

        垃圾收集器检查堆中是否有任何对象不再被应用程序使用。如果存在此类对象,则可以回收这些对象使用的内存。

        【讨论】:

        • @leppie 查看 Gray 的问题评论。 S.
        • 我没有看到任何提到堆栈爬行,这是最基本的“答案”。
        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2010-10-04
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2011-10-15
        • 2016-11-18
        相关资源
        最近更新 更多