Jor*_*ijk 43 .net memory dispose
.NET垃圾收集器最终将释放内存,但如果你想立即恢复内存呢?你需要在课堂MyClass
上使用什么代码来调用
MyClass.Dispose()
Run Code Online (Sandbox Code Playgroud)
并通过变量和对象释放所有已用空间MyClass
?
Cur*_*her 101
IDisposable与释放内存无关.IDisposable是一种用于释放非托管资源的模式- 内存绝对是一种托管资源.
指向GC.Collect()的链接是正确的答案,但Microsoft .NET文档通常不鼓励使用此函数.
编辑:为这个答案赢得了大量的业力,我觉得有责任详细说明,以免.NET资源管理的新人得到错误的印象.
在.NET进程中,有两种资源 - 托管和非托管."托管"意味着运行时控制资源,而"非托管"意味着它是程序员的责任.实际上,我们今天在.NET中只关注一种托管资源 - 内存.程序员告诉运行时分配内存,然后由运行时决定内存何时可以释放..NET用于此目的的机制称为垃圾收集,您只需使用Google即可在互联网上找到有关GC的大量信息.
对于其他类型的资源,.NET对清理它们一无所知,因此必须依赖程序员来做正确的事情.为此,该平台为程序员提供了三个工具:
第一个允许程序员有效地获取资源,使用它然后在同一方法中释放所有资源.
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
Run Code Online (Sandbox Code Playgroud)
如果"AcquireResource"是一个工厂方法(例如)打开文件并且"Dispose"自动关闭文件,则此代码不会泄漏文件资源.但是"tmp"对象本身的内存仍然可以分配.那是因为IDisposable接口绝对没有与垃圾收集器的连接.如果您确实想要确保释放内存,那么您唯一的选择就是调用GC.Collect()
强制垃圾回收.
但是,不能强调这可能不是一个好主意.让垃圾收集器完成它的设计目的,即管理内存通常要好得多.
如果资源使用的时间较长,会使其生命周期跨越多种方法,会发生什么?显然,"using"语句不再适用,因此程序员必须在完成资源时手动调用"Dispose".如果程序员忘记会发生什么?如果没有回退,那么进程或计算机最终可能会耗尽任何未正确释放的资源.
这就是终结器的用武之地.终结器是你的类上与垃圾收集器有特殊关系的方法.GC承诺 - 在为任何类型的对象释放内存之前 - 它将首先给终结器一个机会进行某种清理.
因此,对于文件,理论上我们根本不需要手动关闭文件.我们可以等到垃圾收集器到达它然后让终结器完成工作.不幸的是,这在实践中效果不好,因为垃圾收集器运行不确定.该文件可能会比程序员期望的更长时间保持打开状态.如果有足够的文件保持打开状态,则在尝试打开其他文件时系统可能会失败.
对于大多数资源,我们都需要这两样东西.我们希望一个约定能够说"我们现在已经完成了这个资源",并且我们希望确保如果我们忘记手动执行清理,则至少有一些机会自动进行清理.这就是"IDisposable"模式发挥作用的地方.这是一个允许IDispose和终结器很好地一起玩的约定.您可以通过查看IDisposable的官方文档来了解该模式的工作原理.
结论:如果您真正想要做的就是确保释放内存,那么IDisposable和终结器将无法帮助您.但IDisposable接口是所有.NET程序员都应该理解的极其重要的模式的一部分.
Pat*_*son 22
您只能处置实现IDisposable接口的实例.
强制垃圾收集立即释放(非托管)内存:
GC.Collect();
GC.WaitForPendingFinalizers();
Run Code Online (Sandbox Code Playgroud)
这通常是不好的做法,但是在.NET框架的x64版本中存在一个错误,它使GC在某些情况下表现得很奇怪,然后你可能想要这样做.我不知道这个bug是否已经解决了.有人知道吗?
要处理一个类,你可以这样做:
instance.Dispose();
Run Code Online (Sandbox Code Playgroud)
或者像这样:
using(MyClass instance = new MyClass())
{
// Your cool code.
}
Run Code Online (Sandbox Code Playgroud)
这将在编译时转换为:
MyClass instance = null;
try
{
instance = new MyClass();
// Your cool code.
}
finally
{
if(instance != null)
instance.Dispose();
}
Run Code Online (Sandbox Code Playgroud)
您可以像这样实现IDisposable接口:
public class MyClass : IDisposable
{
private bool disposed;
/// <summary>
/// Construction
/// </summary>
public MyClass()
{
}
/// <summary>
/// Destructor
/// </summary>
~MyClass()
{
this.Dispose(false);
}
/// <summary>
/// The dispose method that implements IDisposable.
/// </summary>
public void Dispose()
{
this.Dispose(true);
GC.SuppressFinalize(this);
}
/// <summary>
/// The virtual dispose method that allows
/// classes inherithed from this one to dispose their resources.
/// </summary>
/// <param name="disposing"></param>
protected virtual void Dispose(bool disposing)
{
if (!disposed)
{
if (disposing)
{
// Dispose managed resources here.
}
// Dispose unmanaged resources here.
}
disposed = true;
}
}
Run Code Online (Sandbox Code Playgroud)
Kei*_*ith 19
对这个问题的回答有点困惑.
标题询问有关处置,但后来说他们想要立即回忆.
.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();
}
Run Code Online (Sandbox Code Playgroud)
规则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
}
}
Run Code Online (Sandbox Code Playgroud)
这并不是很完整,这就是为什么这个例子是密封的.继承类可能需要遵守下一个规则......
规则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);
}
Run Code Online (Sandbox Code Playgroud)
最后这个带有布尔标志的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;
}
Run Code Online (Sandbox Code Playgroud)
请注意,一旦完成所有其他托管代码创建类的实例,就可以像对待任何其他IDisposable一样对待它(规则2和3).
Bri*_*tle 14
是否也适当提一下,处置并不总是指内存?我比文件更频繁地处理资源这样的文件引用.GC.Collect()直接与CLR垃圾收集器相关,可能会也可能不会释放内存(在任务管理器中).它可能会以负面方式影响您的应用程序(例如性能).
在一天结束时,你为什么要立即回忆?如果来自其他地方存在内存压力,操作系统将在大多数情况下为您提供内存.
看看这篇文章
实现Dispose模式,IDisposable和/或终结器与内存被回收时完全无关; 相反,它与告诉GC 如何回收内存有关.当您调用Dispose()时,您无法与GC进行交互.
只有在确定需要(称为内存压力)然后(并且只有那时)才会为未使用的对象释放内存并压缩内存空间时,GC才会运行.
你可以调用GC.Collect(),但你真的不应该,除非有非常充分的理由(这几乎总是"从不").当您强制执行这样的带外收集周期时,实际上会导致GC执行更多工作,最终可能会损害您的应用程序性能.在GC收集周期的持续时间内,您的应用程序实际上处于冻结状态...运行的GC周期越多,应用程序冻结的时间就越长.
也有一些本地的Win32 API调用,你可以对你的自由工作集,但即使是那些应该避免,除非有非常充分的理由这样做.
Gargbage收集运行时背后的整个前提是,您不必担心运行时分配/释放实际内存的时间(尽可能多); 你只需要担心确保你的对象在被问到后知道如何清理它.
我在http://codingcraftsman.wordpress.com/2012/04/25/to-dispose-or-not-to-dispose/上写了析构函数、处置和垃圾收集的摘要
回答原来的问题:
但是,如果您有代码的性能关键部分并且希望减少垃圾收集减慢速度的可能性,则 GC.Collect 可能会很有用。你之前这么称呼。
最重要的是,有一个支持这种模式的论点:
var myBigObject = new MyBigObject(1);
// something happens
myBigObject = new MyBigObject(2);
// at the above line, there are temporarily two big objects in memory and neither can be collected
Run Code Online (Sandbox Code Playgroud)
与
myBigObject = null; // so it could now be collected
myBigObject = new MyBigObject(2);
Run Code Online (Sandbox Code Playgroud)
但主要的答案是垃圾收集只会起作用,除非你乱搞它!
归档时间: |
|
查看次数: |
67378 次 |
最近记录: |