在C#中释放内存的正确方法是什么?

use*_*674 33 c# memory memory-leaks

我在C#中有一个计时器,它在它的方法中执行一些代码.在代码中我使用了几个临时对象.

  1. 如果我Foo o = new Foo();在方法中有类似的东西,这是否意味着每次计时器滴答时,我都在创建一个新对象和对该对象的新引用?

  2. 如果我有string foo = null,然后我只是在foo中添加一些时间,是否与上面相同?

  3. 垃圾收集器是否会删除该对象,并且会不断创建一个或多个引用并保留在内存中?

  4. 如果我只是声明Foo o;并且没有将它指向任何实例,那么当方法结束时是不是处理掉了?

  5. 如果我想确保删除所有内容,那么最好的方法是:

    • 使用方法内的using语句
    • 通过最后调用dispose方法
    • 通过将Foo o;定时器的方法放在外面并仅在o = new Foo()内部进行赋值,因此在方法结束后删除指向该对象的指针,垃圾收集器将删除该对象.

Bri*_*eon 35

1.如果我有类似Foo o = new Foo()的东西; 在方法内部,这是否意味着每次计时器滴答时,我正在创建一个新对象和对该对象的新引用?

是.

2.如果我有字符串foo = null然后我只是在foo中添加一些时间,它是否与上面相同?

如果你问的是行为是否相同那么是.

3.垃圾收集器是否会删除对象,并且会不断创建引用或保留在内存中?

在引用被认为未使用之后,肯定会收集这些对象使用的内存.

如果我只是宣布Foo o; 而不是指向任何实例,是不是在方法结束时处理?

不,因为没有创建对象,所以没有要收集的对象(dispose不是正确的单词).

5.如果我想确保删除所有内容,那么最好的方法是什么

如果对象的类实现了IDisposable那么你肯定想要尽快贪婪地调用Dispose.该using关键字使得这个更容易,因为它要求Dispose一个异常安全的方式自动完成.

除此之外,除了停止使用该对象之外,您无需做任何其他事情.如果引用是局部变量,那么当它超出范围时,它将有资格进行收集.1如果它是类级别变量,那么您可能需要分配null给它以使其符合条件,然后才能使包含类符合条件.


1这在技术上是不正确的(或至少有一点误导).一个对象在超出范围之前很久就有资格进行收集.CLR经过优化,可在检测到不再使用引用时收集内存.在极端情况下,即使其中一个方法仍在执行,CLR也可以收集对象!

更新:

下面的示例演示了GC将收集对象,即使它们仍然在范围内.您必须编译Release版本并在调试器外部运行它.

static void Main(string[] args)
{
    Console.WriteLine("Before allocation");
    var bo = new BigObject();
    Console.WriteLine("After allocation");
    bo.SomeMethod();
    Console.ReadLine();
    // The object is technically in-scope here which means it must still be rooted.
}

private class BigObject
{
    private byte[] LotsOfMemory = new byte[Int32.MaxValue / 4];

    public BigObject()
    {
        Console.WriteLine("BigObject()");
    }

    ~BigObject()
    {
        Console.WriteLine("~BigObject()");
    }

    public void SomeMethod()
    {
        Console.WriteLine("Begin SomeMethod");
        GC.Collect();
        GC.WaitForPendingFinalizers();
        Console.WriteLine("End SomeMethod");
    }
}
Run Code Online (Sandbox Code Playgroud)

在我的机器上,终结器在SomeMethod仍在执行时运行!

  • @Yaur:我发布了一个示例来演示我正在谈论的内容。请注意,我怀疑此行为可能高度依赖于正在测试的 CLR 版本。早期版本可能没有此优化。这是一个相当有趣的演示,展示了 GC 的攻击性。 (2认同)

Yuc*_*uck 15

.NET垃圾收集器会为您处理所有这些.

它能够确定何时不再引用对象,并且(最终)释放已分配给它们的内存.

  • 它会处理所有这些...除非它没有.不用担心内存管理是失控堆增长的好方法. (4认同)

Mar*_*oth 5

一旦超出范围变得无法访问,对象就可以进行垃圾回收(感谢本!).除非垃圾收集器认为您的内存不足,否则不会释放内存.

对于托管资源,垃圾收集器将知道何时,并且您不需要执行任何操作.

对于非托管资源(例如与数据库或打开文件的连接),垃圾收集器无法知道它们消耗了多少内存,这就是您需要手动释放它们的原因(使用dispose,或者更好的是使用块)

如果没有释放对象,或者你有足够的内存并且没有必要,或者你在应用程序中维护它们的引用,因此垃圾收集器不会释放它们(如果你实际使用这个引用你保持)

  • s /超出范围/无法到达/.范围实际上仅与值类型的(未捕获的)局部变量相关. (2认同)