【问题标题】:Returning disposable object from within using block使用块从内部返回一次性对象
【发布时间】:2012-08-29 20:51:53
【问题描述】:

我注意到在分析我的程序时有很多 byte[] 卡在内存中。我进行了一些挖掘,发现大多数以某种方式创建的实例如下:

public byte[] CreateBytes(byte[] bytes)
{
    using (var start = new MemoryStream()) 
    {
        using (var memStr = new MemoryStream(bytes))
        {
            //do stuff
            return start.ToArray();
        }
    }
}

然后将返回的 byte[] 传递给其他方法,并用于从另一个 using 块中创建另一个 MemoryStream

using (var uncompressedStream = new MemoryStream(uncompressedData))
{
    using (var compressedStream = new MemoryStream())
    {
        //Do some compression
    }
}

myObject.Bytes = uncompressedData;
uncompressedData = null;

return myObject;

uncompressedData 是 CreateBytes() 返回的值)。

我的问题是,byte[] 什么时候被清理?我是否特别需要将其设置为 null,如果需要,在哪里?在第二个 using 块之后,我不再需要它,但如果我只是输入 uncompressedData = null;,我不确定这是否会回收内存。

我原以为CreateBytes(byte[] bytes) 中的 using 语句会处理字节,但由于它返回一个引用,这是否会推迟和/或放弃处理?

编辑:我添加了另一行代码。由于我将 uncompressedBtyes 存储在另一个对象中,将 uncompressedData 设置为 null 是没有意义的,并且只要 myObject(或直到 myObject.Bytes 设置为 null),byte[] 就会存在,对吗?

【问题讨论】:

  • using 仅在您的代码中将非托管对象作为MemoryStream 处理掉。 bytes 由 GC 进程管理,即在适当时收集垃圾(非确定性)。

标签: c# memory-management .net-4.0 memory-leaks using


【解决方案1】:

当两个条件都满足时,byte[] 被清理:

  • 没有对该内存块的引用
  • 垃圾收集器决定收集该内存块

GC 根据各种因素在不确定的时间运行。

【讨论】:

  • 未通过根引用引用的循环引用也将被清除。琐碎点。 :)
【解决方案2】:

字节数组,就像内存中的任何其他托管对象一样,一旦不再可以从任何根引用访问,就有资格进行垃圾回收。您可以相信 GC 知道什么时候发生了这种情况,并智能地安排时间来实际清理合格的对象。尽量不要担心何时真正清理了符合清理条件的对象,GC 可能比你更清楚。

【讨论】:

  • 我不担心,我担心的是它没有被清理,因为它显然不符合 GC 的条件(因为它可能在某处被引用)。
  • @Devin 我们没有看到足够的代码来指出您不再需要数组后引用的区域,即使是这种情况。可能只是 GC 还没有清理它,或者您实际上需要将数据保留那么长时间。
  • 我添加了一些代码,这些代码应该能够从本质上展示我的代码中发生了什么。
  • @Devin 不是真的,或者至少还不够。您只是表明您正在将数组分配给另一个变量,并且您没有显示足够的代码来了解另一个变量的生命周期。
【解决方案3】:

除了爱立克回答:

当您确定不再需要 byte[] 时,分配给它 null

这并不能保证内存会被回收现在,也不能保证它会在之后被回收,这只是一种帮助GC识别它的方式一个可供收藏的主题。

【讨论】:

  • 在几乎所有情况下,当您不再需要某些东西时,因为它即将超出范围,因此无需将其设置为 null。如果这不是真的,那么您的变量更有可能是在更高级别的范围内,那么它们应该是。我几乎从来不需要将变量设置为 null 以便收集它。
  • @Servy:同意这一点,但正如我所言,这是复杂交互中基本的良好规则指南,您可能在哪里处理大量嵌套调用,以及您不使用的函数和类型'不在乎(或不能在乎)。如果工作流程中有某个点您确定不再需要这些东西,请将其分配为 null。一般来说,在最常见的情况下,我会同意你的看法。
  • 实际上,您有很多复杂的嵌套调用的情况正是您不需要将变量设置为 null 的时候,因为它们的范围往往是正确的。当您看到数百/千行代码方法在同一范围内执行数十个完全独立的操作时,您往往会看到变量设置为 null。由于每个操作没有不同的范围,因此在开始时创建的局部变量将始终处于活动状态。有些人选择只将变量设置为 null 以便可以释放它们,而不是将代码重构为更多方法。
  • @Servy:当然,通过重构您可以解决软件中的许多问题,而不仅仅是这个问题。当然,我指的是重构不可能或不可取的情况(有风险,没有时间投资等等......)
  • @Servy 我也同意将 items 设置为 null 通常是不必要的。但是,在上述情况下,(如果我理解正确) uncompressedData 将超出范围,但我相信它只是指向在范围之外创建的 byte[],因此 uncompressedData 将不再指向该 byte[] ,但它仍然存在于内存中(我不知道哪里有问题。)
【解决方案4】:

using 语句确实与您返回的 byte 数组无关。

MemoryStream 是一次性的,该对象的处置是 using 语句所管理的(请注意,处置与垃圾收集不同,但通常是其前身)。

当您拨打ToArray() 时,您正在创造一些新的东西,不是一次性的;一旦超出范围,垃圾收集器将在不确定的时间对其进行清理(这通常是一个经过良好优化的过程)。

【讨论】:

    猜你喜欢
    • 2014-04-11
    • 2017-06-17
    • 2018-09-18
    • 1970-01-01
    • 2013-09-24
    • 1970-01-01
    • 2011-07-30
    相关资源
    最近更新 更多