【问题标题】:Trouble freeing up memory used by 100,000 Dictionaries<T,U>无法释放 100,000 个字典<T,U> 使用的内存
【发布时间】:2012-10-20 13:28:34
【问题描述】:

我的代码使用了许多字典,但我在从所有字典中释放内存时遇到了问题。当我investigate using what I learned in this answer 看到 GenericEqualityComparer 在那里时,我怀疑这就是保持此内存在使用中的原因。

谁能证实这一点,或者告诉我如何释放这段内存?

代码

        Console.WriteLine("{0,10}: Start Point", GC.GetTotalMemory(true));
        List<string> t1 = new List<string>();
        Console.WriteLine("{0,10}: <------- Create List", GC.GetTotalMemory(true));
        t1 = null;
        Console.WriteLine("{0,10}: <------- null List", GC.GetTotalMemory(true));

        GC.Collect();
        Console.WriteLine("{0,10}: After GC.Collect", GC.GetTotalMemory(true));
        Dictionary<string, string> t2 = new Dictionary<string, string>();
        Console.WriteLine("{0,10}: <------- Create Dict", GC.GetTotalMemory(true));
        t2 = null;
        Console.WriteLine("{0,10}: <------- null Dict", GC.GetTotalMemory(true));


        GC.Collect();
        Console.WriteLine("{0,10}: After GC.Collect", GC.GetTotalMemory(true));
        Dictionary<string, string> t3 = new Dictionary<string, string>();
        Console.WriteLine("{0,10}: <------- Create Dict", GC.GetTotalMemory(true));
        t3 = null;
        Console.WriteLine("{0,10}: <------- null Dict", GC.GetTotalMemory(true));

        GC.Collect();
        Console.WriteLine("{0,10}: After GC.Collect", GC.GetTotalMemory(true));
        Dictionary<string, string> t4 = new Dictionary<string, string>();
        Console.WriteLine("{0,10}: <------- Create Dict", GC.GetTotalMemory(true));
        t4 = null;
        Console.WriteLine("{0,10}: <------- null Dict", GC.GetTotalMemory(true));

        GC.Collect();
        Console.WriteLine("{0,10}: After GC.Collect", GC.GetTotalMemory(true));
        Dictionary<string, string> t5 = new Dictionary<string, string>();
        Console.WriteLine("{0,10}: <------- Create Dict", GC.GetTotalMemory(true));
        t5 = null;
        Console.WriteLine("{0,10}: <------- null Dict", GC.GetTotalMemory(true));

        GC.Collect();
        Console.WriteLine("{0,10}: After GC.Collect", GC.GetTotalMemory(true));
        Dictionary<string, string> t6 = new Dictionary<string, string>();
        Console.WriteLine("{0,10}: <------- Create Dict", GC.GetTotalMemory(true));
        GC.KeepAlive(t6);
        t6 = null;
        Console.WriteLine("{0,10}: <------- null Dict", GC.GetTotalMemory(true));

        GC.Collect();
        Console.WriteLine("{0,10}: <------- End.", GC.GetTotalMemory(true));

注意:我有两个版本的代码。按照此处关于 SO 的建议,使用 GC.Collect 进行一次编辑,但这没有任何好处。

没有 GC 收集的输出

 95884: Start Point
 97872: <------- Create List
 97888: <------- null List
 97952: <------- Create Dict
 97968: <------- null Dict
 98032: <------- Create Dict
 98048: <------- null Dict
 98112: <------- Create Dict
 98128: <------- null Dict
 98192: <------- Create Dict
 98208: <------- null Dict
 98272: <------- Create Dict
 98288: <------- null Dict

使用 GC 收集输出(根据建议)

 96004: Start Point
 97992: <------- Create List
 98008: <------- null List
 98024: After GC.Collect
 98088: <------- Create Dict
 98104: <------- null Dict
 98120: After GC.Collect
 98184: <------- Create Dict
 98200: <------- null Dict
 98216: After GC.Collect
 98280: <------- Create Dict
 98296: <------- null Dict
 98312: After GC.Collect
 98376: <------- Create Dict
 98392: <------- null Dict
 98408: After GC.Collect
 98472: <------- Create Dict
 98488: <------- null Dict
 98504: <------- End.

发布模式下的输出(根据建议)

 96028: Start Point
 98016: <------- Create List
 98032: <------- null List
 98048: After GC.Collect
 98112: <------- Create Dict
 98128: <------- null Dict
 98144: After GC.Collect
 98208: <------- Create Dict
 98224: <------- null Dict
 98240: After GC.Collect
 98304: <------- Create Dict
 98320: <------- null Dict
 98336: After GC.Collect
 98400: <------- Create Dict
 98416: <------- null Dict
 98432: After GC.Collect
 98496: <------- Create Dict
 98512: <------- null Dict
 98528: <------- End.

在没有 GC 收集的情况下以发布模式输出(根据建议)

 96028: Start Point
 98016: <------- Create List
 98032: <------- null List
 98096: <------- Create Dict
 98112: <------- null Dict
 98176: <------- Create Dict
 98192: <------- null Dict
 98256: <------- Create Dict
 98272: <------- null Dict
 98336: <------- Create Dict
 98352: <------- null Dict
 98416: <------- Create Dict
 98432: <------- null Dict
 98448: <------- End.

【问题讨论】:

  • @Yossarian 我按照您的要求添加了 GC 收集,但现在内存使用率更高。我怀疑我将任何悬而未决的东西推广到 Gen2 并让问题变得更糟
  • 我会说 GC.Collect 会进行完整的收集,即使是 Gen2 对象。

标签: c# .net generics memory-leaks garbage-collection


【解决方案1】:

在调试模式下,GC 不会收集您当前方法范围内的对象,因为对它们的引用仍然存在。尝试构建它以发布,无需调试即可运行并检查结果。

在调试模式下打印为真但在发布时为假的代码:

object obj = new object();
WeakReference reference = new WeakReference(obj);
GC.Collect(0, GCCollectionMode.Forced);
Console.WriteLine(reference.IsAlive);

从 OP 编辑​​:调试模式确实会人为地增加内存使用量。当我运行独立应用程序时,我得到了这个输出。谢谢,我接受这个答案。

 21852: Start Point
 29328: <------- Create List
 29328: <------- null List
 29328: <------- Create Dict
 29328: <------- null Dict
 29328: <------- Create Dict
 29328: <------- null Dict
 29328: <------- Create Dict
 29328: <------- null Dict
 29328: <------- Create Dict
 29328: <------- null Dict
 29376: <------- Create Dict
 29328: <------- null Dict
 29328: <------- End.

【讨论】:

  • @这只能在 .NET 4 及以后的版本中可靠地工作。这也将导致可访问的 Gen0 和 Gen1 对象被提升为 Gen2,这反过来又会使将来释放内存变得更加困难。
  • butirat 谢谢,这成功了。我用输出编辑了你的答案。希望你不要介意。 :)
  • 是的,我同意@GaneshR。您能否删除要求 GC.Collect 的部分,这对我来说是不需要的,可能会引起其他人的困惑。
【解决方案2】:

好吧,你这样做并没有释放内存

value = null;

您只是将值设置为 null,而不是从内存中删除某些内容。 没有明确要求垃圾收集器执行其任务。

GC.Collect();

是您执行其任务所需要的。您通常不会这样做,因为 GC 编写得很好,并且可以选择自己的时间来收集未使用的对象。如果你无缘无故地经常调用它,它也会对你的表现造成很大的影响。还有一组关于 GC 如何以及为什么会收集某些东西的条件。调试、方法、范围等。

【讨论】:

  • 这并不完全正确。 GC.GetTotalMemory(true) 会做垃圾回收,看我的回答。
  • @Yossarian:“垃圾收集器不保证收集所有不可访问的内存。”调试模式和方法范围可能会很好地防止这种情况发生。
【解决方案3】:

来自GC.GetTotalMemory 的文档:

如果 forceFullCollection 参数为真,则此方法等待一个 系统收集垃圾和返回前的短暂间隔 最终确定对象。间隔的持续时间是内部的 由垃圾回收周期数确定的指定限制 完成和恢复的内存量之间的变化 循环。垃圾收集器不保证所有不可访问的 内存被收集。

所以,没有错误,GC 是不确定的,你不能依赖 GetTotalMemory(true) 来清理所有东西。

您需要致电 GC.Collect() 进行完整的垃圾回收。

【讨论】:

    猜你喜欢
    • 2023-03-30
    • 1970-01-01
    • 1970-01-01
    • 2011-05-31
    • 2013-07-30
    • 1970-01-01
    相关资源
    最近更新 更多