【问题标题】:Explicitly freeing memory in c#在 C# 中显式释放内存
【发布时间】:2010-10-23 20:17:59
【问题描述】:

我创建了一个 c# 应用程序,它占用了 150mb 的内存(私有字节),主要是因为字典很大:

Dictionary<string, int> Txns = new Dictionary<string, int>();

我想知道如何释放这个内存。我试过这个:

Txns = null;
GC.Collect();

但这似乎并没有对我的私人字节造成太大影响——它们从 155mb 下降到 145mb。 有什么线索吗?

谢谢

-编辑-

好吧,我对这段代码有更多的运气(它将私有字节减少到 50mb),但为什么呢?

Txns.Clear(); // <- makes all the difference
Txns = null;
GC.Collect();

-编辑-

好吧,对于所有说“不要使用 GC.collect”的人来说,这很公平(我不打算对此进行辩论,只是说你可以看到我的 C 背景),但它并没有真正回答我的问题: 如果我先清除事务列表,为什么垃圾收集器只会释放内存?既然字典已被取消引用,它不应该释放内存吗?

【问题讨论】:

  • GC.Collect() 并不强制在那个时候收集所有可以收集的内存,因为垃圾收集是一个多步骤的过程。我可能会添加一个过程,您通常甚至不需要知道有关...的详细信息
  • 是否需要将Txns设置为null,会有什么不同吗?还是只清除内容就足够了(释放内存)?

标签: c# .net memory memory-leaks profiling


【解决方案1】:

私有字节反映进程的内存使用情况。收集对象时,相关的内存段可能会或可能不会释放给操作系统。 CLR 在操作系统级别管理内存,由于分配和释放内存不是免费的,因此没有理由立即释放每块内存,因为应用程序稍后可能会请求更多内存。

【讨论】:

  • 这听起来很合理,但如果我调用 Txns.Clear(),它确实确​​实将内存释放给操作系统。另外,我真的很想善待操作系统内存,因为我不是唯一使用此服务器的人。
  • 我想我要问的是,为什么 txns.Clear() 将内存归还给操作系统,而对整个字典的引用却没有归还呢?
  • 重点是,在托管应用程序中,您可以从直接处理操作系统内存中删除一步,因为 CLR 会为您执行此操作。您可能能够构建可以观察到您所描述的直接效果的场景,但根据 CLR 的实现细节,这并不能保证像您一样工作。
【解决方案2】:

如果你调用 GC.Collect(),它会开始工作,但它会立即返回,它不会阻塞,所以你看不到它的影响,如果你之后调用 GC.WaitForPendingFinalizers(),它会阻塞你的应用直到 GC.Collect() 完成它的工作

【讨论】:

  • 哎呀!甚至无法开始考虑启动定期(而且经常:它每秒尝试两次!)调用 GC.Collect 然后等待所有待处理的终结器的线程的糟糕程度。您只是对扩展应用程序完全不感兴趣吗?那只是试图隐藏一个不好的症状,糟糕!我想我只需要在这里 -1 ...
  • @peSHIr:同意,但公平地说,它确实明确回答了这个问题。是否是一个好主意是一个不同的问题。
  • @andy:勉强同意。那我应该-1这个问题吗......?也似乎不必要的苛刻和无用。哦,好吧。
  • 我不确定这是否完全正确。垃圾收集将(可能?)唤醒终结器线程,然后它将根据需要运行终结器。因此等待终结器并不是真正等待垃圾收集完成(因为 GC 发生在不同的线程上)
  • 嗨,感谢您的回答,但似乎没有什么不同,因为我尝试了这个并且私有字节没有下降://Txns.Clear(); TXNS =空; for (int x = 0; x 真的不应该调用它,因为我正在取消引用整个字典。
【解决方案3】:

很可能您在其他地方有对字典的隐藏引用。因此字典没有被收集,但如果你Clear()它,内容就会被收集。

正如其他人已经指出的那样,不建议强制 GC。这可能会导致内存被推入不经常收集的更高“代”,从而从长远来看浪费的内存多于获得的内存。

【讨论】:

  • 不,没有对字典的隐藏引用。
  • 您对将对象推向更高代的评论,需要详细说明吗?这听起来像是一个需要注意的问题。
  • 等一下,我想如果你不带参数调用 gc.collect,它只会收集所有代?这不排除将东西推向更高代的问题吗?谢谢。
  • GC.Collect() 确实收集了所有的基因。这里有两个问题。 1)调用一次:仍然引用的内存被向上推,从而延迟了正常的收集,2)定期调用它会浪费更多的cpu,因为gc每次都必须处理所有的gens。所以无论你用 GC.Collect() 做什么,你都会松懈。当然,除非在那些影响对您而言无关紧要的特殊情况下。
  • 在这个阶段运行内存分析器将证明不存在隐藏引用。
【解决方案4】:

从记忆中不确定Dictionary 是否有Dispose(),但它一定有Clear()。在设置对null 的任何引用之前调用其中任何一个。

然后,让垃圾收集器完成它的工作。自己明确地调用GC.Collect() 几乎从来没有是一个好主意,它甚至可能不会做你想要/需要/期望的事情,最终会降低你的性能。静态代码分析 (=FxCop) 不会用 Reliability rule CA2001 警告你这件事,你知道吗?除非您真的知道自己在做什么,否则不要这样做。即使那样也不要这样做。 ;-)

你确定字典有那么大吗?不是只有 10 Mb 的内存,其余的则由您的应用程序占用吗?可能对您有所帮助的问题:您是否使用过探查器来查看实际消耗内存的位置...?

【讨论】:

  • 字典那么大。当我使用 .Clear 更改运行代码时,内存使用量从 160mb 变为 50mb。所以我假设字典是 110mb。
【解决方案5】:

编辑:

公平地说,将引用设置为 null 不会释放内存,它会将其容器分配给不同的地址,在本例中为 null。根据MSDN,调用Clear(),这样做:“Count 属性设置为0,集合元素对其他对象的引用也被释放。容量保持不变。”

...

你永远不应该调用垃圾收集器。您正在使用没有本机资源的托管对象,请相信垃圾收集器会在您之后进行清理。

除了字典的大小之外,您无需担心内存,内存不是您的问题,而是垃圾收集器的问题。

调用Clear() 将删除对内部任何包含对象的引用,但容量保持不变。

从技术角度来看,收集内存是昂贵的,而且操作起来相当耗时。原因是,GC 不仅处理堆内存并清理你的堆,它还对堆进行碎片整理。它会尝试将内存移动到连续的块中,以在某些代码发出大量请求时加快分配速度。

附言您使用 155MB 内存的字典有多大?

【讨论】:

  • 它很大,我正在核对一百万笔交易。
  • 天哪,这是很多交易。我假设这是一次性的,还是您需要定期执行此操作?
  • 实际上我弄错了 - 每天大约六次。
  • 好的,如果是这样,是否有理由必须在代码中完成?为什么不直接使用数据库来处理这些事务,有理由不这样做吗?
【解决方案6】:

你需要恢复内存吗?内存可用,只是没有被回收。您不应该清除字典,对它持有一个weak reference 并让运行时完成它的工作。

如果您想深入了解正在发生的事情,请查看.NET Memory Profiler。这将使您能够准确地了解对象正在发生的事情,它是哪一代,什么正在使用什么内存,等等。 :)

【讨论】:

  • 我不需要返回内存,但是在我们的情况下,如果可能的话,我希望尽快将它还给操作系统。我想这个问题正变得更像是一个理论上的“如何做到这一点?”而不是“我真的需要这样做”的事情......
【解决方案7】:

好吧,我有一个理论... Dictionary 是 KeyValuePair 的集合,它又是一个引用类型。

您的字典包含这些键值对。当你说:

Txns = null

它从那些 KeyValuePair 集合中释放引用“Txns”。但是这些 KeyValuePair 仍然引用了 150 Mb 的实际内存,并且它们在范围内,因此还没有准备好进行垃圾回收。

但是当你使用以下内容时:

Txns.Clear();
Txns = null;
GC.Collect();

这里,clear 方法还从它们各自的 KeyValuePair 对象引用中释放了 150Mb 的数据。因此,这些对象已准备好进行垃圾回收。

这只是我在这里做的一个疯狂的猜测。欢迎评论:)

【讨论】:

  • KeyValuePairs 不在范围内。要将它们包含在范围内,您必须设置其他变量或数组来保存 KeyValuePairs 的副本,类似于:var arrayOfReferencesToKeyValuePairs = Txns.ToArray(); 在他的场景中,一旦Txns 为空,就无法访​​问任何KeyValuePairs 和垃圾收集器看到了。
【解决方案8】:

Windows 有两个内存可用性事件。我希望 CLR 对此作出回应。如果有足够的可用内存,明智的做法是不要运行垃圾收集器。因此,为确保您确实观察到不良的 CLR 行为,请使用另一个使用大量内存的虚拟应用程序重复此测试。

【讨论】:

    猜你喜欢
    • 2013-06-17
    • 1970-01-01
    • 2019-11-14
    • 1970-01-01
    • 1970-01-01
    • 2011-02-12
    • 1970-01-01
    • 2016-04-06
    相关资源
    最近更新 更多