【问题标题】:Understanding Dangling References in C#了解 C# 中的悬空引用
【发布时间】:2014-10-13 16:53:18
【问题描述】:

我正在尝试让自己了解在 C# 等垃圾收集语言中如何发生泄漏或悬空引用。我在网上找到了一个人为的示例,我对其进行了修改和观察(通过任务管理器),每次击键时都会安静地“泄漏”。

来源:

using System;
using System.Collections.Generic;

namespace ConsoleApplication1
{
    public class Something
    {
        int[] things;

        public Something() {
            things = new int[10000000];
            Random rnd = new Random();
            for (int i = 0; i < things.Length; ++i) {
                things[i] = rnd.Next();
            }
        }
    }

    static public class SomethingFactory
    {
        public static Something CreateSomething() {
            Something something = new Something();
            return something;
        }
    }

    class Program
    {
        static void Main(string[] args) {

            while (true) {
                Console.ReadKey();

                Console.WriteLine("Creating...");
                Something s = SomethingFactory.CreateSomething();
            }

        }
    }
}

为什么会出现悬空引用?乍一看,Something s 似乎是在while (true) 的范围内实例化的,并且在块结束后,不应存在对该项目的其他引用。然而,每次击键时,我发现应用程序的内存使用量增加了约 40mb,而且似乎从未下降。

【问题讨论】:

  • 您是在运行调试代码还是附加了调试器?尝试在 Release 模式下不使用调试器运行它。
  • 在调试模式下,变量的范围是到函数的末尾,因此直到 Main() 结束时才被收集
  • 垃圾收集器会在需要清理内存时收集你的垃圾。也许是因为你有足够的内存,它没有被收集。您可以通过将Console.ReadKey(); 替换为if (Console.ReadLine == 'g') GC.Collect(); 来或多或少地“模拟”垃圾收集器。在运行您的应用程序期间,输入“g”以启动垃圾收集器。但是,您永远不应该在您的代码中这样做。垃圾收集器经过优化,可以在必要时运行。手动操作是不好的做法。仅供演示使用。
  • 我根本看不到任何“泄漏”。删除 Console.ReadKey() 调用,以便您可以看到它的长期行为。
  • @mikez 我正在运行Debug 构建。调试器与否,它仍然执行相同的操作,但是 Release 构建按预期执行。内存第一次跳跃约 40mb 并停留在那里。 Debug 有何不同?

标签: c# memory-management garbage-collection


【解决方案1】:

你没有做任何不寻常的事情。 GC 可以在您的示例中回收大量报告为正在使用的内存。但是,请记住,当您在任务管理器中观察内存时,您正在查看进程内存。在托管应用程序中,CLR 代表您的应用程序充当内存管理器。它在进程级别分配和释放内存。

其工作方式是分配内存块以在托管堆上存储对象。当对象数量下降时,这些卡盘最终将再次释放到操作系统。要记住的是,CLR 试图对此很聪明,因此它不会在每次托管堆更改时进行分配和释放。因此,通过查看进程级别的内存使用情况很难理解托管内存的使用情况。

您最有可能看到的是内存已分配但未释放,因为 CLR 还没有任何理由释放这些卡盘。

【讨论】:

  • 感谢您的详尽回复。从概念上讲,这对我来说很有意义。什么会促使 CLR 最终释放这些块?只是在系统运行不足的时候吗?另外,请参阅我上面的评论,参考 @mikez 的理论,说明为什么我的应用由于是 Debug 构建而表现如此。
  • 这将是实现细节。根据我的经验,只要内存没有压力,CLR 就会保留分配的内存,因为释放和重新分配效率低下。正如 Hans 在上面的评论中指出的那样,您需要长期监控系统以了解 CLR 如何处理此问题。
猜你喜欢
  • 2011-03-13
  • 2017-05-12
  • 1970-01-01
  • 1970-01-01
  • 2011-07-14
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多