讨论.NET的替代内存管理策略
讨论垃圾回收器的问题和可能的替代解决方案
关于抽象语言特性的学术讨论
首先,讨论任何编程语言或框架(包括C#和.NET)可能的变化或增强都是可以的。虽然C#和.NET是微软的产品,由那里的产品经理负责和决定C#和.NET的发展方向,但我们可以从计算机科学的角度自由讨论它,将其视为抽象编程语言概念的一种实现。因此,当我们在这里谈论C#时,我们想到的是某种“抽象的C#类语言”,我们不关心讨论的特性是否会被包含在特定产品中。
当我在下面的文本中谈到“.NET框架”时,我指的是广义上的框架,包括“.NET Framework”、“.NET Core”、“.NET”和“Mono”等所有家族成员。
软件工程是一门工程学科,它从现实世界的工程问题中获取灵感和动力,讨论它们,并尝试在抽象层面改进和解决它们,以便在新一代产品中实现。这里讨论的原则大多适用于Java语言,或可应用于未来尚未存在的计算机语言或框架。因此,请将此视为关于该主题的学术讨论。
垃圾回收器怀疑论
即使在我第一次学习垃圾回收器技术时(那是在Java语言中),我就已经是一个“GC怀疑论者”。
的确,它最初解决了一个我们在某些C/C++产品中存在的内存泄漏问题,我曾用当时所有可能的工具,如Purify [5]和BoundChecker [6]追查了数天。
但是,作为一名受过训练的数学家,我立刻感觉到垃圾回收与“图遍历问题”[7]有相似之处,并且遍历的复杂性可能很大。如果有一个勤奋的线程创建对象图(需要遍历),而另一个线程遍历已创建的对象图(为了垃圾回收的目的),很容易发生这样的情况:如果第一个线程需要1个时间单位来创建一些复杂的图,那么第二个线程可能需要10个时间单位来遍历它,以便进行垃圾回收。因此,从一开始就明显看出垃圾回收技术会有一些局限性。
但事实是,线程并非总是在创建新的对象图,它们也做其他事情。所以,垃圾回收技术在这里,它是可用的,它解决了内存泄漏这一大问题,但让我们看看它在广泛的应用中实际上有多实用。
.NET中的垃圾回收器 – 成熟阶段
.NET Framework/.NET Core 现在已经有20年历史了。从它首次发布起,就包含了垃圾回收器技术。我认为我们可以自由地说,GC技术正处于鼎盛时期。多年来,我确信,许多科学家和工程师一直在努力开发和改进GC技术。在未来几年,在不远的将来,出现一些能显著改善GC技术的根本性突破的可能性极低。可能会有一些启发式规则的调整,以适应不断改进的硬件、更多的CPU能力和更多的RAM,但我们目前拥有的GC技术将是未来几年我们所依赖的。
.NET中的垃圾回收器 – 分代和启发式
在 [1] 中,概述了 .NET 垃圾回收器的架构。在这里,我们只提及工程师们多年来能够提出的最佳解决方案是使其“分代”,这本身就已是一种启发式方法。
他们将GC分代的原因是“每次都进行完全垃圾回收实在太昂贵了”([1])。只有一个“常识”的论点是,“在一个GC周期中幸存下来的”对象需要更少地检查清理,并且可以移动到上一代。没有科学/数学算法来支持这一点。创建一个完全相反的程序会很容易。此外,将“对象分成小对象和大对象”([9]),以85KB为界限,并将它们保存在大对象堆 (LOH) 中,这又只是另一种启发式方法。他们这样做的原因希望能更快地压缩内存或更少地压缩内存。
我强调GC架构的许多元素是启发式的,而不是基于算法/可证明的,是因为其后果。后果是启发式GC策略在某些情况下可能会失败,这意味着对于某些C#/.NET程序,GC的性能会很差。
GC 暂停和延迟
GC 的最新版本有两种类型的收集:1) 前台;2) 后台 ([12], [13])。前台 GC 应用于 Gen0 和 Gen1,而后台 GC 仅应用于 Gen2。当前台垃圾收集发生时,所有托管线程都会暂停。后台 GC 允许托管线程在垃圾收集期间继续操作。对于许多应用程序来说,周期性地冻结所有线程以便 GC 可以执行其工作是个坏消息。问题当然是,应用程序线程会暂停多久和多久暂停一次。在应用程序中使用 GC 的实际结果是不可避免的延迟。
调整 GC
可以“调整”GC以适应您的应用程序。您可以选择“低延迟”模式([14]),在该模式下,Gen2的收集被抑制/限制,而Gen0和Gen1的收集仍然继续。此外,还有许多配置选项([11])使您能够影响GC的工作方式。但我认为期望程序员花费时间为每个应用程序调整GC参数是不现实的。当然,认真调整/配置GC的方法将包括使用一些高级内存监控工具进行一些测试-试验-修正工作,如果您有时间的话。
说了这么多,.NET GC 技术是许多工程师经过20年研究后能提出的最佳方案。
编写“GC友好”程序
随着垃圾回收器技术的到来,出现了一股巨大的浪潮,要求程序员改变他们的风格并编写程序,以便它们与GC“合作”,或者更确切地说,“对GC友好”。我对此有些怀疑,因为GC技术被宣传为让程序员更轻松,并在后台透明地工作。现在,你突然要求程序员付出额外的努力,以一种“适应”GC的风格编写程序。一方面,你让程序员摆脱了考虑内存释放的需要,但另一方面,你又给了他们另一个任务,据说更容易,即应用模式来适应垃圾回收。
让我们看看一些建议。
微软的GC性能提示 – 不切实际
在 [1] 中,列出了微软自己关于在GC存在下如何编程的建议。他们引用道:“我们应该尽量避免的事情,以便从回收器中获得最佳性能”
- (避免)过多的分配 – 这几乎是好笑的建议。没有人仅仅为了好玩而分配对象。程序员分配变量/对象是因为程序逻辑需要它们,在严肃的程序中会有很多。这几乎就像他们说:“不要过多使用内存,这样我们友好的GC就不需要辛苦工作了”。
- (避免)过大的分配 – 同样,荒谬的建议。我有一台32GB内存的笔记本电脑,你却告诉我不要创建大对象。我猜GC的问题在于压缩堆,并且移动大对象非常耗时。但是,如果我不密集使用所有这些内存,那又有什么意义呢?
- (避免)过多的指针——我明白,过多的指针/引用意味着复杂的图对象,而遍历复杂的图是耗时的。但如果你的业务逻辑是科学/工程的,你的对象图可能会变得非常复杂。这是一个不切实际的建议。
- (避免)过多的对象写入——同样,他们暗示了你的程序逻辑/业务领域隐含的算法应该是什么样子。如果你需要经常改变对象状态,那就是这样。否则程序将无法完成其主要目的。
- (避免)过多的几乎长生命周期对象——同样,我有32GB内存,如果我的程序逻辑需要,我不想被告知不要在内存中保存太多对象。
- (避免)终结器 – 这是一种语言特性,其存在有充分的理由。如果它存在,为什么要限制其使用。
从有趣的角度来看上述建议,它们几乎就像在说:“不要创建过于复杂、过于庞大、过于复杂的程序,这样我们友好的GC就能快速工作”。但严肃地说,程序员在解决问题时心中有许多其他事情,因此“对GC友好”的额外要求(这会影响他们的程序逻辑/算法)根本不受欢迎。
关于重用对象的建议
有很多关于“重用”已分配对象的建议,以避免重新分配。甚至有相关的模式,如“对象池模式”[15]。首先,它再次将改变程序逻辑和为程序员创造额外工作的负担强加给程序员,例如重置对象状态、池化对象等。有时,这并不现实,你只是需要创建新对象,仅此而已。
Roslyn 编码约定
在 [3] 中,有一些关于Roslyn项目的建议:“Roslyn中性能提升通常归结为一点:确保垃圾回收器做最少可能的工作”。所以,它隐含地承认GC延迟是与性能相关的主要问题。
列出的建议包括:
- 避免使用 LINQ。
- 避免在没有
struct
枚举器的集合上使用foreach
。 - 考虑使用对象池。编译器中有很多使用对象池的例子。
再次,它对程序员使用的技术/工艺施加了限制,而所有这些仅仅是为了适应GC并最大限度地减少其延迟。此外,避免在堆上使用对象/结构而仅在栈上使用对象/结构表明托管堆的解决方案存在问题。
.NET 框架中 GC 的实际问题
在这里,我们将列举一些与GC技术使用相关的问题示例
高并发.NET应用程序中的延迟
在[2]中,有一个很好的例子,正如他们所说,“与.NET GC的战斗”。我将在这里列出作者的结论:
- 如今剩下的延迟大多与GC相关,但比以前短得多。
- 如果您在高并发.NET应用程序中遇到类似问题……您可以使用C++重写组件。
最后一个建议,使用 C++ 语言重写部分代码,有点质疑 .NET GC 对大型应用程序的适用性。
GC 暂停和延迟
GC 会因应用程序线程暂停以执行其工作而导致应用程序产生显著延迟,这已被广泛认可和测量 ([4])。
.NET Framework中GC的现状
在这里,我们将批判性地看待GC技术作为.NET框架的一部分。多年来,GC技术一直是.NET框架的“圣牛”,没有人会说它坏话。这并不是一篇“打倒GC”的文章,而更像是“GC很好,但并非总是如此”。这是我的拙见。
- GC技术对许多C#应用程序,尤其是中小型复杂性/规模的项目来说,运行良好且足够。
- 但对于许多C#应用程序,特别是高复杂性/大型项目,GC技术在延迟方面造成了许多问题,未能成为有效的内存管理策略。
- 通过要求程序员编写“GC友好代码”,责任和工作负担从.NET框架推给了个人程序员。从某种程度上说,这是不公平的,因为现在程序员要为GC技术的局限性以及在.NET框架中选择使用它(这是强加给他们的)负责。更糟糕的是,即使他们遵循所有这些建议与GC合作,仍然无法保证能成功避免GC引起的延迟等问题(参见[2])。此外,让我们记住,GC的主要目的是简化和方便程序员的工作,而不是创建额外的任务来取代简单地清理/释放已使用变量/对象内存的原始任务。许多程序员仍然有能力自己释放内存,只要他们选择的语言提供适当的API。
- 不要被关于GC技术的言论所迷惑,比如“0代、大对象堆”等等。它无疑是一个伟大而巧妙的技术解决方案,但在20年的发展之后,它已经达到了顶峰,我们可以自由地说它有严重的局限性,并非适用于所有情况和所有应用场景的良好解决方案。我个人喜欢使用它并重视它,但有时也希望能有选择使用其他东西的余地。
- 问题在于.NET Framework将GC作为最终且唯一的内存管理解决方案。使用C#等语言的程序员没有选择权,例如选择使用非托管堆,在那里他们将自行承担释放内存的责任,而他们可能完全有能力做到这一点。(这里我们排除了使用AllocHGlobal和类似API的情况,因为它们并没有以一种易于用NewUnmanagedHeap(..type..)之类的方式分配对象的方式集成到C#语言中。)
- .NET Framework选择将其自身锁定为仅使用GC作为内存管理解决方案,这一选择具有严重的局限性,使其继承了这些局限性。实际结果是,由于其局限性,.NET Framework可能并非适用于所有类型应用程序场景的最佳语言/框架选择。这对于雄心勃勃地成为“世界上最好、最快的编程框架……”的.NET Framework来说是一个严重的问题。
- 一个巨大的障碍是,多年来为 C#/.NET 开发的许多库和应用程序框架都基于 GC 技术。即使我们为 C# 语言添加了类似于 `NewUnmanagedHeap(..type..)` / `DeleteUnmangedHeap(..object..)` 的功能,你将在主应用程序代码中使用它,你仍然会受到库中使用的已分配对象造成的 GC 暂停的影响。
- 如果 C#/.NET 框架能够为程序员提供替代的内存管理解决方案,使他们能够克服 GC 的局限性,那将受益匪浅。
- 程序员是一群非常聪明的人。GC 技术让他们变得懒惰,但是,如果需要,他们将能够自己正确地清理/释放内存,只要有合适的 API。他们在 GC 技术出现之前就这样做,现在在其他非 GC 的语言中也这样做。
.NET Framework的替代内存管理策略
大致而言,替代方法可分为两类:
- 单堆解决方案 – 拥有某种托管堆增强解决方案
- 双堆解决方案 – 拥有独立的托管堆和非托管堆
在1)中,想法是拥有一个GC托管堆,该堆通过一些额外的API进行增强,使开发人员能够直接传达某些对象/分配的内存不再需要,从而使释放更高效,并希望GC更快,从而使暂停更少和更短。下面讨论的GCD (*)是朝这个方向的尝试。
在 2) 中,想法是拥有两个堆,即托管堆和非托管堆,程序员可以选择在垃圾回收堆或手动堆中分配对象。通常,设计将允许对两个堆的引用。下面描述的 Snowflake 项目 (***) 就是这样一种解决方案。
注:GCD被发现是未完成的研究
最初,我草拟了一个名为GCD的解决方案的算法,如下所示(*),并将其公开讨论。在[24]下面的评论中,用户“Davin On Life”在其评论中指出,以其呈现的形式,它并没有看起来那么有效。因此,我将其撤回为未完成的研究。
如果您对阅读“存在问题的尝试”不感兴趣,请跳至(***)。
我决定保留GCD解决方案提案的原始文本,原因有二。
- 保留已提出、辩论过并被发现不足的尝试和方法的记录是件好事,这样我们就可以避免再次讨论它们。同时,也能记住它们为何不足,以便我们在新的尝试中加以改进。
- 虽然拟议的GCD解决方案的首次迭代可能存在问题,但也许在随后的迭代中,经过更多的研究,我们将能够改进解决方案并克服问题。
带有删除功能的垃圾回收 (GCD) (*)
在这里,我将提出一种替代的内存管理策略,这是我短暂思考后想到的。同样,这是一个抽象概念,可以应用于任何语言,包括Java和.NET Framework。但是,由于我是一名软件工程师,最近主要在.NET Framework环境中工作,并从中获取灵感,因此我将主要讨论如何将其应用于该特定的C#语言环境。同样,这是一种应用于具体工程问题的推测性抽象思想的阐述,我将其视为一种智力练习,并不意味着我对.NET框架的未来拥有任何权威。
所以,基本上,我建议在C#中除了GC之外,还添加C++风格的delete和析构函数。在接下来的文本中,我们称之为“带删除功能的垃圾回收”(GCD)解决方案。以下是主要的建议原则。
- 在GCD框架中,完全应用托管堆和GC。对象使用“`new`”在托管堆上分配。
- 感兴趣的类被添加C++风格的析构函数,比如说对于类XYZ,命名为`~~XYZ()`。析构函数可以是虚的。
- C# 语言通过“
delete
”运算符得到增强。如果应用于类XYZ
的对象引用,它将调用虚析构函数~~XYZ()
(如果存在)并从托管堆中解除分配对象。在这里,我的意思是即时、同步调用,没有任何“惰性求值”或“延迟执行”。 - 程序员可以随时自由地在任何对象上调用 `delete` 运算符。如果一个对象有析构函数,析构函数会首先被调用。如果从未对某个对象引用调用 `delete`,该对象将受常规 GC 周期的约束。
- 一个大问题是“悬空引用”。如果 `对象 Obj1` 有两个引用,并且其中一个引用被调用了 `delete`,那么另一个引用会发生什么?我目前看到两种方法:1) 使用像“智能指针”([17])这样的技术,而不是立即删除对象;或 2) 删除对象并通过另一个引用报告空引用,我知道这可能导致“空引用异常”。我对此有疑问,并且对 GC 的内部机制不够了解,不知道它是否能轻易支持方法 1),那将是一个“更安全”的解决方案。
- 我在这里就不深入探讨析构函数如何工作等细节了,这些是每个C++程序员都熟知的。在析构函数内部,你可以自由地删除作为原始对象分配一部分的其他对象,也可以选择不关心并将它们留给GC处理。
基本上就是这样。
GCD解决方案的优点
以下是我认为GCD解决方案的优点。
- 首先,没有内存泄漏,与GC解决方案一样。程序员可以自由删除对象,如果他们关心的话;但如果不关心,则由常规GC负责内存释放,因为所有对象都像以前一样在托管堆上。
- 程序员有机会,但没有义务,来处理对象的释放。如果他们干预,对象将立即释放;如果他们选择不干预,GC将负责对象。
- 程序员有机会发现并处理关键分配。如果某个循环分配了相同的对象1000次,一行简单的delete将解决问题,GC将少分析1000个指针,因此预期其工作会快得多。如果程序员发现一些不再需要的大对象,几个delete将使GC的工作更容易,GC仍然需要压缩堆。
- 如果某些对象创建了一个复杂的包含对象的图,这会为GC带来耗时的工作,一个智能析构函数可以拆解该对象,并使GC免于分析复杂的图,从而大大加快速度,并且GC暂停有望变得稀少和更短。
- 程序员没有义务编写“完整”的析构函数,他们可以拆解最重要的部分,例如最大的对象或最复杂的图,并将“小问题”留给GC解决。
- 通过程序员在删除/释放过程中的干预,GC需要分析的对象列表可以更短、更不复杂,从而使GC暂停稀少且更短。
- 对于要求高性能的应用程序,程序员有机会几乎完全承担对象的删除/释放责任,将GC仅作为一种备份策略,这将使GC周期和暂停变得不必要且不存在。通过适当的API,可以将GC置于“非紧急情况不工作”状态,从而使GC暂停和延迟不复存在。
- 在GCD解决方案中,保留了GC的所有优点,此外,如果程序员决定干预代码,性能可以得到提高。
GCD框架中的编程风格
我认为在GCD内存管理框架内开发程序有三种主要风格:
- 完全依赖GC。这与所有程序员迄今在Java或.NET Framework等环境中所做的一样。这可能适用于许多应用程序。对初学者程序员来说很好。对原型设计来说很好。
- 对关键对象进行部分手动释放。 程序员可以决定在他们注意到的热点处进行干预,手动释放关键对象,例如大对象或小对象的多次分配。这可以用最少的工作对性能产生显著影响。他们可以决定不投入太多工作,并将大部分工作留给GC。
- 对对象释放的完全手动控制。 在高性能应用程序、大型应用程序、复杂应用程序中,并且当有足够的合格人员时,程序员可以决定完全或接近完全控制应用程序中的所有释放,以最大化性能。这可能是一个过程,而不是一个大爆炸事件,因为如果他们不能一次性解决所有问题,也没有惩罚,因为GC作为备份始终存在。
从GC到GCD框架的转换
在这里,我们将作为一项智力练习,讨论从GC到GCD框架的过渡会是怎样的。为了具体起见,我们将以.NET框架为例进行讨论。假设GCD被接受为一种内存管理策略。
- 首先,所有在GC框架中开发的程序将继续在GCD框架中工作。所有库也将继续工作。因此,我们与现有的C#/.NET代码库保持向后兼容。
- 对于一个类
XYZ
,析构函数~~XYZ()
与终结器~XYZ()
非常相似,但在性质上有所不同。首先,析构函数需要是virtual
的。其次,终结器的任务是释放非托管资源,而析构函数处理托管和非托管资源。通常,析构函数需要为相应的类调用终结器,并显式删除它关心手动清理的托管资源(对象)。 - 关于C#中的
IDisposable
接口,在我看来,它确实 intended 作为对象的析构函数。为了向后兼容,它可以保持原样,但可能存在一种模式,说明如何将using指令/IDisposable
接口替换为delete
/析构函数。 - 随着时间的推移,程序员可以使用GCD的新功能,并将其应用到应用程序的主体代码中以提高性能。此外,C#/.NET库发布者可以随着时间审查他们的库,并通过一些手动释放调整来改进其代码。库的新版本将只能在支持GCD内存管理的新.NET Framework中使用。
支持GCD框架所需的工具
我认为需要一些工具来支持高效的GCD编程。至少,是我自己想看到的工具。
我记得我曾大量使用C++环境中的工具来定位内存泄漏和跟踪内存分配。那些是Purify [5]和BoundChecker [6],它们拥有某种“代码插桩”技术,并且能够生成一份非常好的内存泄漏列表以及代码中分配发生的位置。几年前我使用过一些C#/.NET内存分析工具,但对最新版本不熟悉。
我希望看到的是能够为GCD环境生成类似列表的工具。目的是支持程序员能够根据需要尽可能精细地调整其应用程序的内存分配/释放。我希望看到的是:
- 当然,所有托管堆上悬而未决的对象列表,以及它们在代码中的分配位置。
- 我还想看到所有被垃圾回收的对象列表,以及它们在代码中的分配位置。我希望看到一份这样的报告:类XVZ的对象在代码第1234行被分配了1000次,并被垃圾回收了1000次。这在C++术语中实际上是GC处理的“内存泄漏”,并且是GCD框架中手动释放的良好候选,这样GC就不需要处理它,这将使GC摆脱额外的工作,并缩短GC暂停时间。
所以,基本上,我希望能轻松地了解GC的工作情况,这样我就可以在需要时手动解决/释放对象,以使GC不会辛苦工作并长时间阻塞/暂停应用程序。熟悉GC架构的人肯定能够指出GC可以提供给程序员的更多有用信息,也许像遍历哪些图花费了大量时间,这样就可以编写并应用一个巧妙的析构函数。关键是优化程序员的工作,这样他们就不必为所有类编写析构函数,而只需为那些给GC带来问题的类编写。除了让GC做更少、更简单的工作之外,我看不出还有什么其他方法可以加速GC的工作。
微软在.NET中尝试非托管堆 (***)
一位匿名读者向我指出了描述微软自己在.NET框架中引入非托管堆的实验的文章,该项目名为“Snowflake”([20], [21], [22])。一群研究人员甚至从.NET代码中创建了一个分支,并实现了一个解决方案并进行了测试。Snowflake项目是一个严肃的研究项目,涉及来自世界顶尖大学的3名研究人员和来自微软研究院的5名研究人员。我估计它可能包括了十几个支持人员(IT、人力资源、项目管理等),持续了3个多月,预算巨大。这意味着,如果你计算所有工时,像[22]这样的研究论文可能花费了100万美元或更多。
以下是该项目的概述。
- 作者们同意“GC极大地提高了程序员的生产力并确保了内存安全。”([22]),但是“...但GC伴随着性能成本...对某些情况可能会有问题。”([21])
- 在他们的设计中,程序员可以在垃圾回收堆或手动堆中分配对象。
- 一个关键的设计目标是保持完全的GC互操作性,允许两个堆之间互相引用。
- 为了解决“悬空引用”问题,他们开发了自己的技术/模式,称为“所有者和防护”(Snowflake API)。该技术/模式类似于危险指针[23]。
- 程序员如何知道哪些对象应该分配在手动堆上?方法是使用堆分析工具,确定GC是否有显著开销,然后识别那些在旧世代中存活并被收集的对象,并寻找候选对象。
- 他们对 .NET CoreCLR 实现的实验结果显示,“峰值工作集节省高达 3 倍,运行时性能提升 2 倍” ([22])。这意味着一些应用程序将使用少 3 倍的内存,并且速度快 2 倍。他们表示性能优势是由于“对于非常大的对象池,GC 会花费大量时间遍历对象图以释放内存” ([21])。
- 该研究文章发布于2017年,但并未提及可能集成到.NET产品中的事宜。
如此巨大的.NET框架进行如此重大的改变现实吗?
嗯,随着C#8和“可空引用类型”([18])的到来,它们现在正成为标准,我惊讶于宣布我们过去多年来一直生活的东西是非自然的勇气和决心。所有这些炒作都是关于将数百万行代码从“string s=null”这样的表达式更改为“string? s=null”。我是在像“`void * p=0`”这样的表达式上学习编程的,所以真的没有看到“十亿美元的错误”[19]有什么问题。但是,为了改变这个范式而付出的努力,不顾破坏如此多现有代码,表明对.NET Framework进行重大改变是可能的。如果以及当他们决定需要新的.NET内存管理策略时,他们已经表明他们可以推动这种改变。
结论
在这篇文章中,我们首先分析了GC技术的现状及其带来的问题。然后,我们讨论了它在.NET框架(包括“.NET framework”、“.NET Core”、“.NET”和“Mono”整个家族)的实际应用中带来的问题,原因在于它是该框架中唯一可能的内存管理策略。
然后,我们讨论了一种替代解决方案,即结合使用垃圾回收与删除/析构函数,这仍然是未完成的研究。
接着,我们谈到了微软自身在该领域进行的一些研究,名为“Snowflake”项目。
我们认为,将GC作为唯一的内存管理策略是.NET框架的一个严重问题。一些替代方案和改进将是有益的,无论是类似于本文提出的GCD框架、微软的“Snowflake”项目,还是完全不同的方法。时间会告诉我们.NET框架将走向何方。
参考文献
- [1] https://docs.microsoft.com/en-us/previous-versions/dotnet/articles/ms973837(v=msdn.10)?redirectedfrom=MSDN#dotnetgcbasics_topic4
- [2] https://samsaffron.com/archive/2011/10/28/in-managed-code-we-trust-our-recent-battles-with-the-net-garbage-collector
- [3] https://mattwarren.org/2014/06/10/roslyn-code-base-performance-lessons-part-2/
- [4] https://mattwarren.org/2014/06/18/measuring-the-impact-of-the-net-garbage-collector/
- [5] https://www.ibm.com/common/ssi/ShowDoc.wss?docURL=/common/ssi/rep_ca/3/897/ENUS204-063/index.html&lang=en-ZZ
- [6] https://en.wikipedia.org/wiki/BoundsChecker
- [7] https://en.wikipedia.org/wiki/Graph_traversal
- [8] https://en.wikipedia.org/wiki/.NET_Framework
- [9] https://docs.microsoft.com/en-us/dotnet/standard/garbage-collection/large-object-heap
- [10] https://docs.microsoft.com/en-us/dotnet/standard/garbage-collection/fundamentals
- [11] https://docs.microsoft.com/en-us/dotnet/core/runtime-config/garbage-collector
- [12] https://docs.microsoft.com/en-us/dotnet/standard/garbage-collection/workstation-server-gc
- [13] https://docs.microsoft.com/en-us/dotnet/standard/garbage-collection/background-gc
- [14] https://docs.microsoft.com/en-us/dotnet/standard/garbage-collection/latency
- [15] https://en.wikipedia.org/wiki/Object_pool_pattern
- [16] https://mattwarren.org/2014/06/23/measuring-the-impact-of-the-net-garbage-collector-an-update/
- [17] https://en.wikipedia.org/wiki/Smart_pointer
- [18] https://docs.microsoft.com/en-us/dotnet/csharp/nullable-references
- [19] https://www.linkedin.com/pulse/20141126171912-7082046-tony-hoare-invention-of-the-null-reference-a-billion-dollar-mistake/
- [20] https://www.microsoft.com/en-us/research/publication/project-snowflake-non-blocking-safe-manual-memory-management-net/
- [21] https://www.infoq.com/news/2017/09/snowflake/
- [22] https://www.microsoft.com/en-us/research/wp-content/uploads/2017/07/snowflake-extended.pdf
- [23] https://en.wikipedia.org/wiki/Hazard_pointer
- [24] https://codeproject.org.cn/Articles/5329775/Discussing-Alternative-Memory-Management-Strategy
历史
- 2022年4月15日:初始版本