【问题标题】:What happen to a variable in the end of a block in managed code托管代码中块末尾的变量会发生什么情况
【发布时间】:2011-05-14 12:48:27
【问题描述】:

块结束后

{
}

块中的变量会发生什么?

{
    int a;
    a=2;
    int b = 3;
}

//What happened now???

以及在此块之后会发生什么

{
    int a=2;
    int b = 3;
}
GC.Collect();

清楚吗? 如果我选择的标签是错误的,请编辑它,或者如果你可以编辑我的问题,请编辑它。

【问题讨论】:

    标签: c# .net memory-management object-lifetime


    【解决方案1】:

    在块的末尾,所有在内部声明的变量都“超出范围”。

    它们很可能会从堆栈中弹出,但细节是优化器的操作。

    由于您的示例变量都是 ints (ValueTypes),因此它们与垃圾收集无关。

    如果我们把最后一个例子改成:

    {
      int a=2;
      var b = new StringBuilder();
      ...
    }
    GC.Collect();
    

    然后 StringBuilder 的内存将在 GC.Collect() 中收集。请注意,普通程序永远不需要调用 GC.Collect()。

    【讨论】:

    【解决方案2】:

    块内的引用变量将超出范围。当被垃圾回收标记时,它们将保持标记,因为它们不再有任何引用。由于您没有任何引用变量,因此 GC 不会参与其中。

    在某个时刻,垃圾收集器将运行并决定是否可以清除引用变量使用的内存。这取决于它们所处的世代,因此它们可能不会立即被删除。

    调用GC.Collect(); 只是强制GC 运行,尽管收集不确定。对于大多数应用程序来说,这不是必需的。

    【讨论】:

    • 我知道 GC.Collect() 做了什么。
    • @LightWing - 你知道,但不是每个读过这个问题的人都知道。无论如何 - 你没有明确表示你知道。
    • 1) 垃圾回收不适用于像 int 这样的值类型。 2) 垃圾收集发生的点是任何“确定”的。这才是真正的重点。
    • 引用没有“标记为收集”当它们的变量超出范围时。如果您认为标记和清除收集器的“标记”阶段是如何工作的,或者您错误地认为 .NET 使用引用计数收集语义,那么您就误解了它是如何工作的。相反,当 GC 运行时,所有 对象被标记为删除,然后已知活动对象的传递闭包将其标记删除。所有保持标记的对象都将被删除。 (或移至终结器队列。)
    【解决方案3】:

    在块的末尾,在块内声明的变量超出范围。如果它们是值类型(如 int),它们只会从堆栈中弹出。如果它们是引用类型(像大多数其他对象一样,例如StringBuilder),那么它们将不再被任何东西引用(除非你将它传递给仍在范围内的块之外的东西)并且垃圾收集器稍后会得到它.

    如果您有访问稀缺资源的对象,例如各种基于 Stream 的类(或任何实现 IDisposable 的类),那么您应该将其放在这样的 using 语句中:

    using (Stream s = GetStream())
    {
        // Do something with the stream.
    }
    

    在 using 块的末尾,然后调用 Dispose 方法并释放所有资源(例如文件处理程序、数据库连接、大块内存等)

    一般来说,您不需要了解垃圾收集器何时运行或释放内存。当我在 2002 年从 C++ 迁移到 .NET 时,这对我来说是最难理解的事情之一,因为我已经习惯于在堆上创建的任何对象上调用 delete

    您不再需要担心这些。即使您忘记调用 dispose,垃圾收集器最终也会得到它(尽管您可能不想让该文件句柄保持不必要的时间,因此 IDisposable

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2011-11-20
      • 2015-03-28
      • 2018-04-07
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多