C# 中释放内存的正确方法是什么
我有一个 C# 定时器,它在它的方法中执行一些代码。在代码中我使用了几个临时对象。
如果我在方法内有类似
Foo o = new Foo();
的内容,是否意味着每次计时器计时时,我都会创建一个新对象和对该对象的新引用对象?如果我有
string foo = null
然后我只是在 foo 中放入一些临时内容,它与上面相同吗?垃圾收集器是否删除了该对象,并且引用或对象不断创建并保留在内存中?
如果我只是声明
Foo o;
并且不将其指向任何实例,那么方法结束时不是会被释放吗?如果我想确保删除所有内容,最好的方法是什么:
- 在方法内使用 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.
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?If I have
string foo = null
and then I just put something temporal in foo, is it the same as above?Does the garbage collector ever delete the object and the reference or objects are continually created and stay in memory?
If I just declare
Foo o;
and not point it to any instance, isn't that disposed when the method ends?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 assignmento = new Foo()
inside, so then the pointer to the object is deleted after the method ends, the garbage collector will delete the object.
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(8)
是的。
如果您问行为是否相同,那么是的。
在引用被视为未使用后,这些对象使用的内存肯定会被收集。
不,因为没有创建对象,所以没有要收集的对象(“处置”不是正确的词)。
了
IDisposable
,那么您肯定希望尽快贪婪地调用Dispose
。using
关键字使这变得更容易,因为它以异常安全的方式自动调用Dispose
。除此之外,除了停止使用该对象之外,您实际上不需要做任何其他事情。如果引用是局部变量,那么当它超出范围时,它将有资格被收集。1如果它是类级别变量,那么您可能需要分配
null
使其在包含类符合资格之前使其符合资格。1这在技术上是不正确的(或者至少有点误导)。对象在超出范围之前就可以进行收集。 CLR 经过优化,可以在检测到不再使用引用时收集内存。在极端情况下,即使对象的方法之一仍在执行,CLR 也可以收集对象!
更新:
下面的示例演示了 GC 将收集对象,即使它们可能仍在范围内。您必须编译发布版本并在调试器之外运行它。
在我的机器上,终结器在
SomeMethod
仍在执行时运行!Yes.
If you are asking if the behavior is the same then yes.
The memory used by those objects is most certainly collected after the references are deemed to be unused.
No, since no object was created then there is no object to collect (dispose is not the right word).
If the object's class implements
IDisposable
then you certainly want to greedily callDispose
as soon as possible. Theusing
keyword makes this easier because it callsDispose
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.
On my machine the finalizer is run while
SomeMethod
is still executing!.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.
一旦对象
超出范围变得无法访问,就可以进行垃圾回收(谢谢ben!)。除非垃圾收集器认为您的内存不足,否则不会释放内存。对于托管资源,垃圾收集器会知道这是什么时候,并且您不需要执行任何操作。
对于非托管资源(例如与数据库的连接或打开的文件),垃圾收集器无法知道它们消耗了多少内存,这就是为什么您需要手动释放它们(使用 dispose,或者更好的是 using 块)
如果对象没有被释放,要么你有足够的内存剩余并且没有必要,要么你在应用程序中维护对它们的引用,因此垃圾收集器不会释放它们(如果你实际上使用这个引用你维持)
Objects are eligable for garbage collection once they
go out of scopebecome 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)
让我们一一回答您的问题。
Let's answer your questions one by one.
以下是一个快速概述:
最后一件事:如果您声明
Foo foo;
而不分配它,您不必担心 - 不会泄漏任何内容。如果 Foo 是引用类型,则不会创建任何内容。如果 Foo 是值类型,它会在堆栈上分配,因此会自动清理。Here's a quick overview:
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.using
语句只是在退出时调用IDisposable
对象的 dispose,因此这相当于您的第二个要点。两者都会表明您已完成该对象,并告诉 GC 您已准备好放弃它。覆盖对该对象的唯一引用也会产生类似的效果。Foo
variable will never ever take up memory.using
statement simply calls dispose on anIDisposable
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.垃圾收集器将会出现并清理任何不再引用它的东西。除非您在
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 ao = new Foo()
each time around, it should all work out fine.正如 Brian 指出的,GC 可以收集任何无法访问的内容,包括仍在范围内的对象,甚至在这些对象的实例方法仍在执行时。考虑以下代码:
如果使用调试构建、附加调试器或使用未注释的指定行运行,则 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:
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.