【问题标题】:Using floating curly braces to free variables early in C#在 C# 早期使用浮动花括号释放变量
【发布时间】:2012-09-04 22:42:32
【问题描述】:

C# 变量在声明类型的地方实例化(例如string s;)并在当前范围的右大括号处释放:

// Operates with Q memory
void FantasyMethod() {
    var o = new BigObject();

    {
        var temp = new BigObject();
        Populate(temp); // Populates o1 with N megabytes of data

        o = PerformSomeOperationsOn(temp); // Returns a BigObject of size M (M is close to N)

        // Currently, M+N memory is occupied, we have Q-M-N free
    }

    // Let's tell the garbage collector to catch up
    GC.Collect();
    GC.WaitForPendingFinalizers();

    // Currently, M memory is occupied

    DoUsefulStuffWith(o); // This method can only work if at least Q-M-N/2 memory is free
}

这样做的一个好处是我可以在函数返回之前释放大变量。在上面的(微不足道的)块中,我通过在不再需要一个大变量时立即处理我有限的可用内存。

  1. 以上是否正确?
  2. 这样做是个好主意(我对赞成和反对的论点感兴趣,而不是个人意见或偏好)?提取裸括号块作为一种方法会降低内存使用效率吗?如果出于可读性原因我不想创建新方法怎么办?

【问题讨论】:

    标签: c# optimization coding-style garbage-collection curly-braces


    【解决方案1】:

    这样做的一个好处是我可以在函数返回之前释放大变量。

    没有。 C# 不是 C++,对象没有析构函数,并且不能保证对象在离开其声明范围且不存在对其的有效引用时会被回收。

    如果您需要这种级别的可预测性,那么您不应该使用托管语言。确实存在有助于减轻 C# 中的内存压力的技术,但它们并不经常需要,而且您永远无法获得像 C 或 C++ 这样的语言所提供的控制级别。

    根据您的编辑:

    GC.Collect尝试运行 GC 通行证,但不能保证。 GC.WaitForPendingFinalizers 阻塞,直到所有标记为终结的对象都运行了它们的终结器。

    如果一个对象实现了终结器并且没有通过调用 SuppressFinalize 禁用终结器,则该对象将放置在标记为准备终结的对象列表中。垃圾收集器为此列表中的对象调用 Finalize 方法并从列表中删除条目。此方法会阻塞,直到所有终结器都运行完成。

    【讨论】:

    • 我更改了问题,以减少是否执行垃圾收集的歧义。希望 GC 调用是正确的。
    • +0:不完全正确 - 在这个特定示例中,GC 资格没有变化,因为 temp 在最后一次使用后立即有资格获得 GC,在示例中与内部块的结尾一致,但也会在同一个地方没有内部块。 (注意这条关于发布/优化 JIT 行为的评论,在调试模式下的范围更长)
    • @AlexeiLevenkov:很好,我删除了它。我对 GC 不够了解,无法提出这样的要求。谢谢。
    • 所以在我的编辑中,我使用大括号和 GC 调用允许为内存密集型 DoUsefulStuffWith 方法释放内存是否正确?如果我没有使用花括号,GC不会收集temp吗?
    • @Superbest:在这种情况下,限制范围是没有帮助的。正如 Alexei 指出的“temp 在最后一次使用后立即有资格获得 GC”。您确实需要分析您的应用程序,托管语言中的内存使用情况并不像您想要的那样具有确定性。
    【解决方案2】:

    您的假设并不完全正确。与 C++ 不同,对象在超出范围时不会立即销毁(或者,如果我们想学究气,请调用析构函数)。所有可以保证的是,如果在绝对没有引用指向封闭范围内实例化的对象的情况下发生 GC 扫描,那么该对象将被收集

    即使您没有将对象的使用包含在显式范围内,编译器也已经有足够的信息“知道”该对象无论如何都没有被使用/引用,因此您不会做任何事情恩惠。

    【讨论】:

      【解决方案3】:

      这是不正确的,假设BigObject 不是值类型。如果BigObject 是一个类,那么它将始终存在于托管堆上,并且在超出范围后不会被确定性地处理掉。 GC 在另一个线程中运行,您无法预测它何时会进行收集。

      【讨论】:

        【解决方案4】:

        我从未听说将变量放在块内会释放变量。还有你说的释放是什么意思?内存由垃圾收集器管理,它将决定何时释放内存。

        如果“变量”持有资源,那么它应该实现 IDisposable; http://msdn.microsoft.com/en-us/library/system.idisposable.aspx) 你可以:

        using (var b = new DisposableObject())
        {
           ...
        }
        

        【讨论】:

        • 在我的例子中,不能假设 BigObject 实现了 IDisposable。
        • 我的评论说“应该实现 IDisposable”然后......另外,如果它没有实现接口,那么编译器会抱怨。
        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 2014-05-31
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2021-09-17
        • 2015-08-10
        • 2018-05-02
        相关资源
        最近更新 更多