.NET垃圾收集器最终将释放内存,但如果你想立即恢复内存呢?你需要在课堂MyClass
上使用什么代码来调用
MyClass.Dispose()
并通过变量和对象释放所有已用空间MyClass
?
IDisposable与释放内存无关.IDisposable是一种用于释放非托管资源的模式- 内存绝对是一种托管资源.
指向GC.Collect()的链接是正确的答案,但Microsoft .NET文档通常不鼓励使用此函数.
编辑:为这个答案赢得了大量的业力,我觉得有责任详细说明,以免.NET资源管理的新人得到错误的印象.
在.NET进程中,有两种资源 - 托管和非托管."托管"意味着运行时控制资源,而"非托管"意味着它是程序员的责任.实际上,我们今天在.NET中只关注一种托管资源 - 内存.程序员告诉运行时分配内存,然后由运行时决定内存何时可以释放..NET用于此目的的机制称为垃圾收集,您只需使用Google即可在互联网上找到有关GC的大量信息.
对于其他类型的资源,.NET对清理它们一无所知,因此必须依赖程序员来做正确的事情.为此,该平台为程序员提供了三个工具:
IDisposable接口和VB和C#中的"using"语句
终结
由许多BCL类实现的IDisposable模式
第一个允许程序员有效地获取资源,使用它然后在同一方法中释放所有资源.
using (DisposableObject tmp = DisposableObject.AcquireResource()) { // Do something with tmp } // At this point, tmp.Dispose() will automatically have been called // BUT, tmp may still a perfectly valid object that still takes up memory
如果"AcquireResource"是一个工厂方法(例如)打开文件并且"Dispose"自动关闭文件,则此代码不会泄漏文件资源.但是"tmp"对象本身的内存仍然可以分配.那是因为IDisposable接口绝对没有与垃圾收集器的连接.如果您确实想要确保释放内存,那么您唯一的选择就是调用GC.Collect()
强制垃圾回收.
但是,不能强调这可能不是一个好主意.让垃圾收集器完成它的设计目的,即管理内存通常要好得多.
如果资源使用的时间较长,会使其生命周期跨越多种方法,会发生什么?显然,"using"语句不再适用,因此程序员必须在完成资源时手动调用"Dispose".如果程序员忘记会发生什么?如果没有回退,那么进程或计算机最终可能会耗尽任何未正确释放的资源.
这就是终结器的用武之地.终结器是你的类上与垃圾收集器有特殊关系的方法.GC承诺 - 在为任何类型的对象释放内存之前 - 它将首先给终结器一个机会进行某种清理.
因此,对于文件,理论上我们根本不需要手动关闭文件.我们可以等到垃圾收集器到达它然后让终结器完成工作.不幸的是,这在实践中效果不好,因为垃圾收集器运行不确定.该文件可能会比程序员期望的更长时间保持打开状态.如果有足够的文件保持打开状态,则在尝试打开其他文件时系统可能会失败.
对于大多数资源,我们都需要这两样东西.我们希望一个约定能够说"我们现在已经完成了这个资源",并且我们希望确保如果我们忘记手动执行清理,则至少有一些机会自动进行清理.这就是"IDisposable"模式发挥作用的地方.这是一个允许IDispose和终结器很好地一起玩的约定.您可以通过查看IDisposable的官方文档来了解该模式的工作原理.
结论:如果您真正想要做的就是确保释放内存,那么IDisposable和终结器将无法帮助您.但IDisposable接口是所有.NET程序员都应该理解的极其重要的模式的一部分.
您只能处置实现IDisposable接口的实例.
强制垃圾收集立即释放(非托管)内存:
GC.Collect(); GC.WaitForPendingFinalizers();
这通常是不好的做法,但是在.NET框架的x64版本中存在一个错误,它使GC在某些情况下表现得很奇怪,然后你可能想要这样做.我不知道这个bug是否已经解决了.有人知道吗?
要处理一个类,你可以这样做:
instance.Dispose();
或者像这样:
using(MyClass instance = new MyClass()) { // Your cool code. }
这将在编译时转换为:
MyClass instance = null; try { instance = new MyClass(); // Your cool code. } finally { if(instance != null) instance.Dispose(); }
您可以像这样实现IDisposable接口:
public class MyClass : IDisposable { private bool disposed; ////// Construction /// public MyClass() { } ////// Destructor /// ~MyClass() { this.Dispose(false); } ////// The dispose method that implements IDisposable. /// public void Dispose() { this.Dispose(true); GC.SuppressFinalize(this); } ////// The virtual dispose method that allows /// classes inherithed from this one to dispose their resources. /// /// protected virtual void Dispose(bool disposing) { if (!disposed) { if (disposing) { // Dispose managed resources here. } // Dispose unmanaged resources here. } disposed = true; } }
对这个问题的回答有点困惑.
标题询问有关处置,但后来说他们想要立即回忆.
.Net是管理的,这意味着当您编写.Net应用程序时,您不需要直接担心内存,但成本是您无法直接控制内存.
.Net决定什么时候最好清理和释放内存,而不是你作为.Net编码器.
这Dispose
是一种告诉.Net的方法,你已经完成了某些事情,但它实际上不会释放内存,直到这是最好的时间.
基本上.Net实际上会收集内存,因为它最容易收集内存 - 它非常擅长决定何时.除非你正在写一些非常耗费内存的东西,否则你通常不需要推翻它(这也是游戏通常不是用.Net编写的部分原因 - 它们需要完全控制)
在.Net中,您可以GC.Collect()
立即强制使用它,但这几乎总是不好的做法.如果.Net尚未清理它,那意味着它不是一个特别好的时机.
GC.Collect()
选择.Net识别的对象.如果您还没有处理需要它的对象.Net可能会决定保留该对象.这意味着GC.Collect()
只有正确实施一次性实例才有效.
GC.Collect()
是不是对正确使用IDisposable的一个替代品.
因此Dispose和内存没有直接关系,但它们并不需要.正确处理将使您的.Net应用程序更高效,因此使用更少的内存.
在.Net中99%的时间是以下最佳做法:
规则1:如果你不处理任何非托管或实现的东西,IDisposable
那么不要担心Dispose.
规则2:如果你有一个实现IDisposable的局部变量,请确保你在当前范围内删除它:
//using is best practice using( SqlConnection con = new SqlConnection("my con str" ) ) { //do stuff } //this is what 'using' actually compiles to: SqlConnection con = new SqlConnection("my con str" ) ; try { //do stuff } finally { con.Dispose(); }
规则3:如果一个类有一个实现IDisposable的属性或成员变量,那么该类也应该实现IDisposable.在该类的Dispose方法中,您还可以处理您的IDisposable属性:
//rather basic example public sealed MyClass : IDisposable { //this connection is disposable public SqlConnection MyConnection { get; set; } //make sure this gets rid of it too public Dispose() { //if we still have a connection dispose it if( MyConnection != null ) MyConnection.Dispose(); //note that the connection might have already been disposed //always write disposals so that they can be called again } }
这并不是很完整,这就是为什么这个例子是密封的.继承类可能需要遵守下一个规则......
规则4:如果类使用非托管资源,则实现IDispose 并添加终结器.
.Net无法对非托管资源做任何事情,所以现在我们谈论的是内存.如果你不清理它可能会导致内存泄漏.
Dispose方法需要处理托管和非托管资源.
终结者是一个安全捕获 - 它确保如果其他人创建和您的类的实例并且未能处置它,那么"危险的" 非托管资源仍然可以被.Net清理.
~MyClass() { //calls a protected method //the false tells this method //not to bother with managed //resources this.Dispose(false); } public void Dispose() { //calls the same method //passed true to tell it to //clean up managed and unmanaged this.Dispose(true); //as dispose has been correctly //called we don't need the //'backup' finaliser GC.SuppressFinalize(this); }
最后这个带有布尔标志的Dispose重载:
protected virtual void Dispose(bool disposing) { //check this hasn't been called already //remember that Dispose can be called again if (!disposed) { //this is passed true in the regular Dispose if (disposing) { // Dispose managed resources here. } //both regular Dispose and the finaliser //will hit this code // Dispose unmanaged resources here. } disposed = true; }
请注意,一旦完成所有其他托管代码创建类的实例,就可以像对待任何其他IDisposable一样对待它(规则2和3).
是否也适当提一下,处置并不总是指内存?我比文件更频繁地处理资源这样的文件引用.GC.Collect()直接与CLR垃圾收集器相关,可能会也可能不会释放内存(在任务管理器中).它可能会以负面方式影响您的应用程序(例如性能).
在一天结束时,你为什么要立即回忆?如果来自其他地方存在内存压力,操作系统将在大多数情况下为您提供内存.
看看这篇文章
实现Dispose模式,IDisposable和/或终结器与内存被回收时完全无关; 相反,它与告诉GC 如何回收内存有关.当您调用Dispose()时,您无法与GC进行交互.
只有在确定需要(称为内存压力)然后(并且只有那时)才会为未使用的对象释放内存并压缩内存空间时,GC才会运行.
你可以调用GC.Collect(),但你真的不应该,除非有非常充分的理由(这几乎总是"从不").当您强制执行这样的带外收集周期时,实际上会导致GC执行更多工作,最终可能会损害您的应用程序性能.在GC收集周期的持续时间内,您的应用程序实际上处于冻结状态...运行的GC周期越多,应用程序冻结的时间就越长.
也有一些本地的Win32 API调用,你可以对你的自由工作集,但即使是那些应该避免,除非有非常充分的理由这样做.
Gargbage收集运行时背后的整个前提是,您不必担心运行时分配/释放实际内存的时间(尽可能多); 你只需要担心确保你的对象在被问到后知道如何清理它.