.NET中的垃圾回收器

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

2
你看过MSDN吗?http://msdn.microsoft.com/en-us/library/ee787088.aspx - Gary.S
4个回答

7
简而言之:每个应用程序都有一组根。这些根标识存储位置,这些位置可以是托管堆上的对象或设置为null的对象。
当垃圾收集器开始运行时,它假定堆中的所有对象都是垃圾。
垃圾收集器开始遍历根并构建从根可达的所有对象的图形。
所有不可访问的对象都被移除(内存被释放)。
以上内容摘自http://msdn.microsoft.com/en-us/magazine/bb985010.aspx - 一个关于垃圾回收的好文章。对你来说“有趣”的部分是“垃圾回收算法”。它并不是一个很长的部分。

3
没有提到Raymond Chen的一系列博客文章,.NET中垃圾收集的讨论将不完整: 以下是系列文章中第一篇的引用:

当你问别人什么是垃圾收集时,你得到的答案可能会按如下方式:“垃圾收集是操作环境自动回收程序不再使用的内存。 它通过跟踪从根开始的内存来识别哪些对象是可访问的”。

这种描述混淆了机制与目标。 这就像说消防员的工作是“驾驶红色卡车并喷水”。 这是对消防员所做的事情的描述,但它忽略了工作的重点(即扑灭火灾,更普遍地说,消防安全)。

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

一个正确编写的程序不能假定finalizers将永远运行。

 

代码块中的对象可以在调用函数期间变得可回收。

 

方法的参数可以在执行方法时变得可回收。

 

奇怪的现实世界类比:垃圾收集器可以在潜水员最后一次接触跳板时收集您的跳板,即使潜水员仍在空中!

最简洁的:

不要把GC视为跟踪根。 把GC看作是删除你不再使用的东西。


0

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

垃圾回收器的类型

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

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

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

enter image description here

非并发垃圾回收 非并发垃圾回收会暂停所有非垃圾回收线程,在垃圾回收期间完全暂停应用程序。

后台垃圾回收 后台垃圾回收是并发垃圾回收的替代方案。它类似于并发垃圾回收,但允许中断正在进行的第二代垃圾回收,并临时阻塞程序执行,以进行一代和二代垃圾回收。完成一代和二代垃圾回收后,程序执行和第二代垃圾回收都会继续。这进一步缩短了因垃圾回收而导致程序执行暂停的时间。

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

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

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

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

  • 第0代:最年轻的代,包含短寿命对象(例如,临时变量或新分配的对象,除非它们是大对象,在这种情况下,它们会在第2代集合中进入大对象堆)
  • 第1代:如果某些第0代对象占用的空间在垃圾回收运行时未被释放,则这些对象将移动到第1代并包含短寿命对象。
  • 第2代:如果某些第1代对象占用的空间在下一次垃圾回收运行时未被释放,则这些对象将移动到第2代。此代包含长寿命对象(即在服务器应用程序中包含静态数据的对象,该数据在进程的持续时间内保持活动状态)。

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

垃圾回收器阶段

垃圾回收由以下条件之一触发:

  • 系统物理内存不足。这可以通过操作系统的低内存通知或主机指示的低内存来检测到。

  • 托管堆上分配对象使用的内存超过可接受的阈值。该阈值在进程运行时不断调整。

  • 调用GC.Collect方法。在几乎所有情况下,您不必调用此方法,因为垃圾回收器会持续运行。

此时,垃圾回收器经过以下阶段:

  • 标记阶段:垃圾回收器通过以下信息创建所有活动对象的列表(不在列表中的所有对象都有可能被删除),以确定对象是否存活:

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

  • 资源

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


    -4

    @leppie,请查看Gray. S的问题评论。 - Rauf
    我没有看到任何关于堆栈爬取的提及,这是最基本的“答案”。 - leppie

    网页内容由stack overflow 提供, 点击上面的
    可以查看英文原文,
    原文链接