2

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. 这样做是个好主意吗(我对赞成和反对的论点感兴趣,而不是个人意见或偏好)?提取裸括号块作为一种方法会降低内存使用效率吗?如果出于可读性原因我不想制作新方法怎么办?
4

4 回答 4

5

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

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

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

根据您的编辑:

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

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

于 2012-09-11T23:24:23.263 回答
2

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

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

于 2012-09-11T23:29:34.277 回答
0

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

于 2012-09-11T23:25:18.770 回答
0

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

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

using (var b = new DisposableObject())
{
   ...
}
于 2012-09-11T23:28:24.713 回答