C#中的垃圾回收和作用域如何工作?

18 投票
4 回答
17914 浏览
提问于 2025-04-16 14:20

我正在学习C#,之前是用Python的,想了解一下C#的垃圾回收是怎么工作的。我发现一旦弄明白了Python背后发生了什么,我对它的理解就深了很多,所以我希望能避免在学习Python时犯的那些新手错误。

我找不到关于什么时候会进行垃圾回收的清晰解释,心里有一些疑问,比如:

  1. “当一个对象的最后一个引用超出作用域时,会发生什么?”这个对象会被垃圾回收吗?还是说当你再次进入定义它的作用域时,它依然存在?
  2. “引用计数是在什么时刻减少的?”这让我想知道它是否使用引用计数,或者是其他什么技术……

如果能回答这些问题,或者给我一个清晰简明的概述,讲讲到底发生了什么,那我会非常感激(会给你点赞),如果能把C#和Python的做法进行比较就更好了。我不关心哪个更好,只想了解细节。另外,如果能在我原来的帖子上回答我,也会非常感谢……

4 个回答

5

垃圾回收并不是因为引用超出作用域就会触发。通常,垃圾回收是在为新对象分配存储空间时触发的,特别是当第一代的预算用完了的时候。也就是说,可能会有一段时间的延迟,直到对象可以被垃圾回收,但实际上它们并不会立刻被回收。正如其他人所提到的,CLR(公共语言运行库)并不使用引用计数的方法。相反,它采用的是标记和清扫的方式。

如果你想了解垃圾回收的详细信息,可以参考杰弗里·里克特的书《CLR via C#》。这本书详细讲解了堆是如何划分的,以及垃圾回收是如何工作的。如果你对.NET的实现细节感兴趣,这本书非常推荐。

6

在某个不确定的时间点,当最后一个对某个对象的引用消失后,这个对象就会被回收。

你第一个问题的第二部分不太合理。
如果你能够再次进入定义这个对象的范围(比如说,一个lambda表达式),那么显然仍然有对这个对象的引用。

垃圾回收(GC)根本不使用引用计数。
相反,它使用的是一种叫做标记-清扫算法

25

dotnet的垃圾回收(GC)引擎是一种标记-清除的机制,而不是像你在Python中常见的引用计数机制。系统并不会记录一个变量被引用了多少次,而是在需要回收内存时,进行一次“回收”,它会标记所有当前可以访问的指针,并删除那些无法访问的指针(也就是说,这些指针已经超出了作用范围)。

你可以在这里了解更多关于它是如何工作的:
http://msdn.microsoft.com/en-us/library/ee787088.aspx

这个系统通过从特定的“根”位置开始查找“可达”的对象,比如全局对象和栈上的对象,然后追踪这些对象引用的所有对象,再追踪那些对象引用的对象,依此类推,直到构建出一棵完整的树。这比听起来要快。

撰写回答