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

发布于 2024-11-08 02:39:46 字数 591 浏览 0 评论 0原文

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

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

  2. 如果我有 string foo = null 然后我只是在 foo 中放入一些临时内容,它与上面相同吗?

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

  4. 如果我只是声明 Foo o; 并且不将其指向任何实例,那么方法结束时不是会被释放吗?

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

    • 在方法内使用 using 语句
    • 最后调用 dispose 方法
    • Foo o; 放在计时器的方法之外,并在内部进行赋值 o = new Foo(),这样在计时器的方法之后,指向该对象的指针就会被删除方法结束,垃圾收集器将删除该对象。

I have a timer in C# which executes some code inside it's method. Inside the code I'm using several temporary objects.

  1. If I have something like Foo o = new Foo(); inside the method, does that mean that each time the timer ticks, I'm creating a new object and a new reference to that object?

  2. If I have string foo = null and then I just put something temporal in foo, is it the same as above?

  3. Does the garbage collector ever delete the object and the reference or objects are continually created and stay in memory?

  4. If I just declare Foo o; and not point it to any instance, isn't that disposed when the method ends?

  5. If I want to ensure that everything is deleted, what is the best way of doing it:

    • with the using statement inside the method
    • by calling dispose method at the end
    • by putting Foo o; outside the timer's method and just make the assignment o = new Foo() inside, so then the pointer to the object is deleted after the method ends, the garbage collector will delete the object.

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(8

浴红衣 2024-11-15 02:39:46

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

是的。

2.如果我有字符串 foo = null 然后我只是在 foo 中放入一些临时的东西,
和上面一样吗?

如果您问行为是否相同,那么是的。

3.垃圾收集器是否删除过对象和引用?
对象不断被创建并且
留在记忆中吗?

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

4.如果我只是声明 Foo o;并且不将其指向任何实例,不是吗
方法结束时处理?

不,因为没有创建对象,所以没有要收集的对象(“处置”不是正确的词)。

5.如果我想确保所有内容都被删除,最好的方法是什么
做起来

IDisposable,那么您肯定希望尽快贪婪地调用Disposeusing 关键字使这变得更容易,因为它以异常安全的方式自动调用 Dispose

除此之外,除了停止使用该对象之外,您实际上不需要做任何其他事情。如果引用是局部变量,那么当它超出范围时,它将有资格被收集。1如果它是类级别变量,那么您可能需要分配 null使其在包含类符合资格之前使其符合资格。


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

更新:

下面的示例演示了 GC 将收集对象,即使它们可能仍在范围内。您必须编译发布版本并在调试器之外运行它。

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");
    }
}

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

1.If I have something like Foo o = new Foo(); inside the method, does that
mean that each time the timer ticks,
I'm creating a new object and a new
reference to that object?

Yes.

2.If I have string foo = null and then I just put something temporal in foo,
is it the same as above?

If you are asking if the behavior is the same then yes.

3.Does the garbage collector ever delete the object and the reference or
objects are continually created and
stay in memory?

The memory used by those objects is most certainly collected after the references are deemed to be unused.

4.If I just declare Foo o; and not point it to any instance, isn't that
disposed when the method ends?

No, since no object was created then there is no object to collect (dispose is not the right word).

5.If I want to ensure that everything is deleted, what is the best way of
doing it

If the object's class implements IDisposable then you certainly want to greedily call Dispose as soon as possible. The using keyword makes this easier because it calls Dispose automatically in an exception-safe way.

Other than that there really is nothing else you need to do except to stop using the object. If the reference is a local variable then when it goes out of scope it will be eligible for collection.1 If it is a class level variable then you may need to assign null to it to make it eligible before the containing class is eligible.


1This is technically incorrect (or at least a little misleading). An object can be eligible for collection long before it goes out of scope. The CLR is optimized to collect memory when it detects that a reference is no longer used. In extreme cases the CLR can collect an object even while one of its methods is still executing!

Update:

Here is an example that demonstrates that the GC will collect objects even though they may still be in-scope. You have to compile a Release build and run this outside of the debugger.

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");
    }
}

On my machine the finalizer is run while SomeMethod is still executing!

百思不得你姐 2024-11-15 02:39:46

.NET 垃圾收集器会为您处理这一切。

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

The .NET garbage collector takes care of all this for you.

It is able to determine when objects are no longer referenced and will (eventually) free the memory that had been allocated to them.

七秒鱼° 2024-11-15 02:39:46

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

对于托管资源,垃圾收集器会知道这是什么时候,并且您不需要执行任何操作。

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

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

Objects are eligable for garbage collection once they go out of scope become unreachable (thanks ben!). The memory won't be freed unless the garbage collector believes you are running out of memory.

For managed resources, the garbage collector will know when this is, and you don't need to do anything.

For unmanaged resources (such as connections to databases or opened files) the garbage collector has no way of knowing how much memory they are consuming, and that is why you need to free them manually (using dispose, or much better still the using block)

If objects are not being freed, either you have plenty of memory left and there is no need, or you are maintaining a reference to them in your application, and therefore the garbage collector will not free them (in case you actually use this reference you maintained)

当梦初醒 2024-11-15 02:39:46

让我们一一回答您的问题。

  1. 是的,每当执行此语句时,您都会创建一个新对象,但是,当您退出该方法时,它会“超出范围”并且有资格进行垃圾回收。
  2. 嗯,这与 #1 相同,只是您使用了字符串类型。字符串类型是不可变的,每次赋值时都会得到一个新对象。
  3. 是的,垃圾收集器会收集超出范围的对象,除非您将对象分配给具有大范围的变量(例如类变量)。
  4. 是的。
  5. using 语句仅适用于实现 IDisposable 接口的对象。如果是这种情况,那么对于方法范围内的对象来说,使用是最好的。除非有充分的理由,否则不要将 Foo o 放在更大的范围内。最好将任何变量的范围限制在有意义的最小范围内。

Let's answer your questions one by one.

  1. Yes, you make a new object whenever this statement is executed, however, it goes "out of scope" when you exit the method and it is eligible for garbage collection.
  2. Well this would be the same as #1, except that you've used a string type. A string type is immutable and you get a new object every time you make an assignment.
  3. Yes the garbage collector collects the out of scope objects, unless you assign the object to a variable with a large scope such as class variable.
  4. Yes.
  5. The using statement only applies to objects that implement the IDisposable interface. If that is the case, by all means using is best for objects within a method's scope. Don't put Foo o at a larger scope unless you have a good reason to do so. It is best to limit the scope of any variable to the smallest scope that makes sense.
千笙结 2024-11-15 02:39:46

以下是一个快速概述:

  • 一旦引用消失,您的对象将可能被垃圾收集。
  • 如果所有对垃圾的引用确实消失了,您只能依靠统计收集来保持堆大小正常。换句话说,无法保证特定对象将被垃圾收集。
    • 因此,您的终结器也永远不会被保证被调用。避免终结器。
  • 两种常见的泄漏源:
    • 事件处理程序和委托是引用。如果您订阅某个对象的事件,则您正在引用该对象。如果您有一个对象方法的委托,那么您就是在引用它。
    • 根据定义,非托管资源不会自动收集。这就是 IDisposable 模式的用途。
  • 最后,如果您想要一个不会阻止对象被收集的引用,请查看 WeakReference。

最后一件事:如果您声明 Foo foo; 而不分配它,您不必担心 - 不会泄漏任何内容。如果 Foo 是引用类型,则不会创建任何内容。如果 Foo 是值类型,它会在堆栈上分配,因此会自动清理。

Here's a quick overview:

  • Once references are gone, your object will likely be garbage collected.
  • You can only count on statistical collection that keeps your heap size normal provided all references to garbage are really gone. In other words, there is no guarantee a specific object will ever be garbage collected.
    • It follows that your finalizer will also never be guaranteed to be called. Avoid finalizers.
  • Two common sources of leaks:
    • Event handlers and delegates are references. If you subscribe to an event of an object, you are referencing to it. If you have a delegate to an object's method, you are referencing it.
    • Unmanaged resources, by definition, are not automatically collected. This is what the IDisposable pattern is for.
  • Finally, if you want a reference that does not prevent the object from getting collected, look into WeakReference.

One last thing: If you declare Foo foo; without assigning it you don't have to worry - nothing is leaked. If Foo is a reference type, nothing was created. If Foo is a value type, it is allocated on the stack and thus will automatically be cleaned up.

幸福丶如此 2024-11-15 02:39:46
  1. 是的,
  2. 你说的“相同”是什么意思?每次运行该方法时都会重新执行该方法。
  3. 是的,.Net 垃圾收集器使用一种算法,该算法从任何全局/范围内变量开始,在递归地跟踪找到的任何引用的同时遍历它们,并删除内存中被视为无法访问的任何对象。 请参阅此处了解有关垃圾收集的更多详细信息
  4. 是的,当方法退出时,方法中声明的所有变量的内存都会被释放,因为它们都是不可访问的。此外,任何已声明但从未使用的变量都将被编译器优化,因此实际上您的 Foo 变量永远不会占用内存。
  5. using 语句只是在退出时调用 IDisposable 对象的 dispose,因此这相当于您的第二个要点。两者都会表明您已完成该对象,并告诉 GC 您已准备好放弃它。覆盖对该对象的唯一引用也会产生类似的效果。
  1. Yes
  2. What do you mean by the same? It will be re-executed every time the method is run.
  3. Yes, the .Net garbage collector uses an algorithm that starts with any global/in-scope variables, traverses them while following any reference it finds recursively, and deletes any object in memory deemed to be unreachable. see here for more detail on Garbage Collection
  4. Yes, the memory from all variables declared in a method is released when the method exits as they are all unreachable. In addition, any variables that are declared but never used will be optimized out by the compiler, so in reality your Foo variable will never ever take up memory.
  5. the using statement simply calls dispose on an IDisposable object when it exits, so this is equivalent to your second bullet point. Both will indicate that you are done with the object and tell the GC that you are ready to let go of it. Overwriting the only reference to the object will have a similar effect.
嘦怹 2024-11-15 02:39:46

垃圾收集器将会出现并清理任何不再引用它的东西。除非您在 Foo 中有非托管资源,否则调用 Dispose 或对其使用 using 语句并不会真正帮助您。

我相当确定这适用,因为它仍然是 C# 中的。但是,我使用 XNA 参加了游戏设计课程,我们花了一些时间讨论 C# 的垃圾收集器。垃圾收集的成本很高,因为您必须检查是否有任何对要收集的对象的引用。因此,GC 会尝试尽可能推迟此操作。因此,只要当程序达到 700MB 时物理内存没有耗尽,就可能只是 GC 比较懒,还没有关心它。

但是,如果您只是在循环外使用 Foo o 并每次都创建一个 o = new Foo(),那么一切都应该正常。

The garbage collector will come around and clean up anything that no longer has references to it. Unless you have unmanaged resources inside Foo, calling Dispose or using a using statement on it won't really help you much.

I'm fairly sure this applies, since it was still in C#. But, I took a game design course using XNA and we spent some time talking about the garbage collector for C#. Garbage collecting is expensive, since you have to check if you have any references to the object you want to collect. So, the GC tries to put this off as long as possible. So, as long as you weren't running out of physical memory when your program went to 700MB, it might just be the GC being lazy and not worrying about it yet.

But, if you just use Foo o outside the loop and create a o = new Foo() each time around, it should all work out fine.

流年里的时光 2024-11-15 02:39:46

正如 Brian 指出的,GC 可以收集任何无法访问的内容,包括仍在范围内的对象,甚至在这些对象的实例方法仍在执行时。考虑以下代码:

class foo
{
    static int liveFooInstances;

    public foo()
    {
        Interlocked.Increment(ref foo.liveFooInstances);
    }

    public void TestMethod()
    {
        Console.WriteLine("entering method");
        while (Interlocked.CompareExchange(ref foo.liveFooInstances, 1, 1) == 1)
        {
            Console.WriteLine("running GC.Collect");
            GC.Collect();
            GC.WaitForPendingFinalizers();
        }
        Console.WriteLine("exiting method");
    }

    ~foo()
    {
        Console.WriteLine("in ~foo");
        Interlocked.Decrement(ref foo.liveFooInstances);
    }

}

class Program
{

    static void Main(string[] args)
    {
        foo aFoo = new foo();
        aFoo.TestMethod();
        //Console.WriteLine(aFoo.ToString()); // if this line is uncommented TestMethod will never return
    }
}

如果使用调试构建、附加调试器或使用未注释的指定行运行,则 TestMethod 将永远不会返回。但是在没有附加调试器的情况下运行 TestMethod 将返回。

As Brian points out the GC can collect anything that is unreachable including objects that are still in scope and even while instance methods of those objects are still executing. consider the following code:

class foo
{
    static int liveFooInstances;

    public foo()
    {
        Interlocked.Increment(ref foo.liveFooInstances);
    }

    public void TestMethod()
    {
        Console.WriteLine("entering method");
        while (Interlocked.CompareExchange(ref foo.liveFooInstances, 1, 1) == 1)
        {
            Console.WriteLine("running GC.Collect");
            GC.Collect();
            GC.WaitForPendingFinalizers();
        }
        Console.WriteLine("exiting method");
    }

    ~foo()
    {
        Console.WriteLine("in ~foo");
        Interlocked.Decrement(ref foo.liveFooInstances);
    }

}

class Program
{

    static void Main(string[] args)
    {
        foo aFoo = new foo();
        aFoo.TestMethod();
        //Console.WriteLine(aFoo.ToString()); // if this line is uncommented TestMethod will never return
    }
}

if run with a debug build, with the debugger attached, or with the specified line uncommented TestMethod will never return. But running without a debugger attached TestMethod will return.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文