CJ7*_*CJ7 300 .net c# garbage-collection dispose
您是否需要处理对象并将它们设置为null,或者当垃圾收集器超出范围时它们是否会将它们清理干净?
Zac*_*son 236
当物体不再被使用以及垃圾收集器看起来合适时,物体将被清理干净.有时,您可能需要将对象设置null
为使其超出范围(例如,您不再需要其值的静态字段),但总体而言通常无需设置null
.
关于处理对象,我同意@Andre.如果对象是在您不再需要它时处置IDisposable
它是一个好主意,特别是如果对象使用非托管资源.不处理非托管资源会导致内存泄漏.
using
一旦程序离开using
语句的范围,您可以使用该语句自动处理对象.
using (MyIDisposableObject obj = new MyIDisposableObject())
{
// use the object here
} // the object is disposed here
Run Code Online (Sandbox Code Playgroud)
这在功能上等同于:
MyIDisposableObject obj;
try
{
obj = new MyIDisposableObject();
}
finally
{
if (obj != null)
{
((IDisposable)obj).Dispose();
}
}
Run Code Online (Sandbox Code Playgroud)
Igo*_*aka 133
对象永远不会像在C++中那样超出C#的范围.它们在不再使用时由垃圾收集器自动处理.这是一种比C++更复杂的方法,其中变量的范围完全是确定性的.CLR垃圾收集器主动遍历已创建的所有对象,如果正在使用它们则可以解决.
对象可以在一个函数中"超出范围",但如果返回其值,则GC将查看调用函数是否保留在返回值上.
设置对象引用null
是不必要的,因为垃圾收集通过计算其他对象引用哪些对象来工作.
在实践中,你不必担心破坏,它只是工作,它是伟大的:)
Dispose
必须在IDisposable
完成使用它们时实现的所有对象上调用.通常你会使用using
这些对象的块,如下所示:
using (var ms = new MemoryStream()) {
//...
}
Run Code Online (Sandbox Code Playgroud)
编辑可变范围.Craig已经询问变量范围是否对对象生命周期有任何影响.为了正确解释CLR的这个方面,我需要从C++和C#中解释一些概念.
在这两种语言中,变量只能在与定义相同的范围内使用 - 类,函数或括号括起来的语句块.然而,细微差别在于,在C#中,无法在嵌套块中重新定义变量.
在C++中,这是完全合法的:
int iVal = 8;
//iVal == 8
if (iVal == 8){
int iVal = 5;
//iVal == 5
}
//iVal == 8
Run Code Online (Sandbox Code Playgroud)
但是在C#中,您会遇到编译器错误:
int iVal = 8;
if(iVal == 8) {
int iVal = 5; //error CS0136: A local variable named 'iVal' cannot be declared in this scope because it would give a different meaning to 'iVal', which is already used in a 'parent or current' scope to denote something else
}
Run Code Online (Sandbox Code Playgroud)
如果查看生成的MSIL,这是有意义的 - 函数使用的所有变量都是在函数的开头定义的.看看这个功能:
public static void Scope() {
int iVal = 8;
if(iVal == 8) {
int iVal2 = 5;
}
}
Run Code Online (Sandbox Code Playgroud)
以下是生成的IL.请注意,在if块中定义的iVal2实际上是在功能级别定义的.实际上,这意味着就可变生命周期而言,C#仅具有类和功能级别范围.
.method public hidebysig static void Scope() cil managed
{
// Code size 19 (0x13)
.maxstack 2
.locals init ([0] int32 iVal,
[1] int32 iVal2,
[2] bool CS$4$0000)
//Function IL - omitted
} // end of method Test2::Scope
Run Code Online (Sandbox Code Playgroud)
每当在堆栈上分配的C++变量超出范围时,它就会被破坏.请记住,在C++中,您可以在堆栈或堆上创建对象.当您在堆栈上创建它们时,一旦执行离开作用域,它们就会从堆栈中弹出并被销毁.
if (true) {
MyClass stackObj; //created on the stack
MyClass heapObj = new MyClass(); //created on the heap
obj.doSomething();
} //<-- stackObj is destroyed
//heapObj still lives
Run Code Online (Sandbox Code Playgroud)
在堆上创建C++对象时,必须显式销毁它们,否则会导致内存泄漏.虽然堆栈变量没有这样的问题.
在CLR中,始终在托管堆上创建对象(即引用类型).对象创建语法进一步强化了这一点.请考虑此代码段.
MyClass stackObj;
Run Code Online (Sandbox Code Playgroud)
在C++中,这将MyClass
在堆栈上创建一个实例并调用其默认构造函数.在C#中,它将创建对MyClass
不指向任何内容的类的引用.创建类实例的唯一方法是使用new
运算符:
MyClass stackObj = new MyClass();
Run Code Online (Sandbox Code Playgroud)
在某种程度上,C#对象很像使用new
C++中的语法创建的对象- 它们是在堆上创建的,但与C++对象不同,它们由运行时管理,因此您不必担心破坏它们.
由于对象总是在堆上,因此对象引用(即指针)超出范围的事实变得没有实际意义.确定是否要收集对象涉及的因素多于简单地存在对象的引用.
Jon Skeet 将Java中的对象引用与附加到气球(即对象)的字符串进行了比较.相同的类比适用于C#对象引用.它们只是指向包含该对象的堆的位置.因此,将其设置为null对对象生命周期没有立即影响,气球继续存在,直到GC"弹出"它.
继续按照气球的比喻,似乎合乎逻辑的是,一旦气球没有任何附加条件,它就会被摧毁.实际上,这正是引用计数对象在非托管语言中的工作方式.除非这种方法不适用于循环引用.想象一下两个气球通过一个字符串连接在一起,但气球都没有任何其他字符串.在简单的引用计数规则下,它们都会继续存在,即使整个气球组是"孤立的".
.NET对象很像屋顶下的氦气球.当屋顶打开(GC运行)时 - 未使用的气球会漂浮,即使可能有一组气球系在一起.
.NET GC使用世代GC和标记和扫描的组合.分代方法涉及运行时有利于检查最近分配的对象,因为它们更可能未被使用,并且标记和扫描涉及运行时通过整个对象图并且如果存在未使用的对象组则进行计算.这充分解决了循环依赖问题.
此外,.NET GC在另一个线程(所谓的终结器线程)上运行,因为它有很多工作要做,在主线程上这样做会中断你的程序.
Bri*_*eon 17
正如其他人所说,Dispose
如果该类实现,你肯定想要调用IDisposable
.我对此采取了相当严格的立场.有些人可能会声称,呼吁Dispose
上DataSet
,例如,是毫无意义的,因为他们拆开它,看到它没有做任何有意义.但是,我认为这个论点中存在很多谬误.
请阅读此内容,以便受尊敬的人就此主题进行有趣的辩论.然后在这里阅读我的推理,为什么我认为杰弗里里希特在错误的阵营.
现在,关于你是否应该设置引用null
.答案是不.让我用以下代码说明我的观点.
public static void Main()
{
Object a = new Object();
Console.WriteLine("object created");
DoSomething(a);
Console.WriteLine("object used");
a = null;
Console.WriteLine("reference set to null");
}
Run Code Online (Sandbox Code Playgroud)
那么你认为引用的对象什么时候a
有资格收集?如果你说完电话a = null
后你就错了.如果您在Main
方法完成后说,那么您也错了.正确的答案是它有资格在通话期间收集DoSomething
.那是对的.在引用设置之前,甚至在完成调用之前,它是合格的.这是因为JIT编译器可以识别对象引用何时不再解除引用,即使它们仍然是root的.null
DoSomething
EMP*_*EMP 13
您永远不需要在C#中将对象设置为null.编译器和运行时将负责确定它们何时不在范围内.
是的,您应该处理实现IDisposable的对象.
Chr*_*ich 11
如果对象实现IDisposable
,那么是的,你应该处理它.该对象可能会挂起到本机资源(文件句柄,OS对象),否则可能无法立即释放.这可能导致资源匮乏,文件锁定问题以及其他可能无法避免的细微错误.
另请参阅在MSDN上实现Dispose方法.
Yor*_*ort 11
我同意这里的常见答案是肯定你应该处理,并且通常不应该将变量设置为null ...但我想指出dispose主要不是关于内存管理.是的,它可以帮助(有时确实)内存管理,但它的主要目的是为您提供确定性的稀缺资源发布.
例如,如果您打开硬件端口(例如串行),TCP/IP套接字,文件(独占访问模式)甚至是数据库连接,您现在已经阻止任何其他代码使用这些代码,直到它们被释放.处理通常会释放这些物品(以及GDI和其他"os"手柄等,其中有1000种可用,但总体上仍然有限).如果您没有在所有者对象上调用dipose并显式释放这些资源,那么尝试将来再次打开相同的资源(或另一个程序),打开尝试将失败,因为未处理的未收集对象仍然打开该项目.当然,当GC收集项目时(如果Dispose模式已经正确实现),资源将被释放......但是你不知道什么时候会这样,所以你不要 知道何时重新打开该资源是安全的.这是Dispose解决的主要问题.当然,释放这些句柄通常也会释放内存,从不释放它们可能永远不会释放内存......因此所有关于内存泄漏或内存清理延迟的讨论.
我已经看到了这个导致问题的现实世界的例子.例如,我看到ASP.Net Web应用程序最终无法连接到数据库(尽管很短的时间,或者直到Web服务器进程重新启动),因为sql server的"连接池已满"...即,如此多的连接已经创建,并且在如此短的时间内没有明确释放,因此无法创建新的连接,并且池中的许多连接虽然不活动,但仍然被未填充和未收集的对象引用,因此可以'重复使用.在必要时正确配置数据库连接可确保不会发生此问题(至少除非您具有非常高的并发访问权限).
如果他们实现IDisposable接口,那么你应该处理它们.垃圾收集器将处理其余的事情.
编辑:最好是using
在处理一次性物品时使用该命令:
using(var con = new SqlConnection("..")){ ...
Run Code Online (Sandbox Code Playgroud)
总是调用 dispose。不值得冒这个险。应尊重大型托管企业应用程序。不能做任何假设,否则它会回来咬你。
不要听leppie的。
很多对象实际上并没有实现 IDisposable,因此您不必担心它们。如果它们真的超出范围,它们将被自动释放。此外,我从未遇到过必须将某些内容设置为空的情况。
可能发生的一件事是许多对象可以保持打开状态。这会大大增加应用程序的内存使用量。有时很难确定这是否真的是内存泄漏,或者您的应用程序是否只是在做很多事情。
内存配置文件工具可以帮助解决此类问题,但可能会很棘手。
此外,始终取消订阅不需要的事件。还要小心 WPF 绑定和控件。不是通常的情况,但我遇到了一种情况,我有一个绑定到底层对象的 WPF 控件。底层对象很大,占用了大量内存。WPF 控件正在被一个新实例替换,而旧的控件由于某种原因仍然存在。这导致了大量内存泄漏。
在 hindsite 中,代码写得不好,但关键是您要确保未使用的东西超出范围。使用内存分析器需要很长时间才能找到那个,因为很难知道内存中的哪些内容是有效的,哪些内容不应该存在。
当一个对象实现时,IDisposable
您应该调用Dispose
(或者Close
,在某些情况下,它将为您调用Dispose)。
通常,您不必将对象设置为null
,因为GC将知道不再使用某个对象。
将对象设置为时,有一个例外null
。当我从数据库中检索许多需要处理的对象并将它们存储在集合(或数组)中时。完成“工作”后,我将对象设置为null
,因为GC不知道我已经完成了工作。
例:
using (var db = GetDatabase()) {
// Retrieves array of keys
var keys = db.GetRecords(mySelection);
for(int i = 0; i < keys.Length; i++) {
var record = db.GetRecord(keys[i]);
record.DoWork();
keys[i] = null; // GC can dispose of key now
// The record had gone out of scope automatically,
// and does not need any special treatment
}
} // end using => db.Dispose is called
Run Code Online (Sandbox Code Playgroud)
归档时间: |
|
查看次数: |
269435 次 |
最近记录: |