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