Any sense to set obj = null(Nothing) in Dispose()?
在Dispose()方法中,是否有必要将自定义对象设置为null(vb.net中的Nothing?这能防止内存泄漏吗?还是没用?!
让我们考虑两个例子:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27
| public class Foo : IDisposable
{
private Bar bar; // standard custom .NET object
public Foo(Bar bar) {
this.bar = bar;
}
public void Dispose() {
bar = null; // any sense?
}
}
public class Foo : RichTextBox
{
// this could be also: GDI+, TCP socket, SQl Connection, other"heavy" object
private Bitmap backImage;
public Foo(Bitmap backImage) {
this.backImage = backImage;
}
protected override void Dispose(bool disposing) {
if (disposing) {
backImage = null; // any sense?
}
}
} |
型
我个人倾向于;有两个原因:
型
- 对对象调用Dispose()后,您多长时间保留一次对这些对象的引用?
- @布莱恩-注意"意外"和"事件"两个词;注意,我不一定是编写使用我的组件的代码的人。我不能修改他们的代码,但我可以使我的代码行为良好。
- @戴夫-澄清
- 对不起,如果出了问题。我不反对这种做法。我更喜欢没有这些冗余的稍微简单的代码。
- 如果您无论如何都需要实现IDisposable(需要释放一些特定的资源),那么我假设这是您要做的事情,因此您最好是彻底的。大概您没有在任何地方实现IDisposable,所以您可以这样做。
- + 1。在Dispose方法(如果存在)中,应始终将事件设置为空,以防订阅服务器忘记解除自身的连接。我已经看到它发生在每个项目上,当事件没有被清除时,就会出现内存泄漏。
型
Dispose()的目的是允许清理垃圾收集器未处理的资源。对象由GC负责,因此在正常情况下,实际上不需要将引用设置为空。
例外情况是,如果您希望调用者调用Dispose,并在调用后保留实例。在这种情况下,最好将内部引用设置为空。但是,可释放实例通常是同时释放和释放的。在这些情况下,这不会有很大的区别。
- 在第二个示例中,我有一个位图,建议将其释放()。现在,由于位图不是由对象foo创建的,而是刚刚传入参数,所以我不能这样做。我想至少把它设为空…
- 您总是必须决定在何处清除依赖项。如果您知道位图没有在其他地方使用,foo应该调用Dispose()。否则,它应该离开它,让调用者处理清理。将本地引用设置为空没有其他好处。当foo的实例被回收时,位图的实例也会被回收,除非调用方仍然持有对它的引用。
- @serhio-如果希望在foo中使用完位图对象后立即释放该对象使用的资源(没有其他人使用它),则foo.dispose应调用backimage.dispose
- 也许这也指TCP套接字、SQL连接等重对象?因为我只是不知道什么时候设计我的foo对象会不会被我的"重对象"外部使用,也许我不能调用Dispose。
- @是的,只在一个完全属于你的类的对象上调用Dispose。
- 将字段设置为空将取消该字段。您可能没有在Dispose中对它做任何操作;但是在GC决定它不是Dispose之前,该引用是以它的容器为根的。将其设置为空将减轻GC的决定,并在可能的最早时间打开对象。请参阅Marc的答案。
- @彼得里奇是对的,但问题是这是否能防止内存泄漏。对于托管对象,将引用设置为空可能使GC能够更快地收集对象,但不需要这样做。一旦一个对象被释放,GC将声明它。
- 可以说,托管代码中没有"内存泄漏",所以无论您做什么都不重要…但是,如果您将"内存泄漏"定义为不尽快回收的内存,那么不打开对象意味着您有"内存泄漏"。
- @彼得里奇:你是在谈论这个具体的案例,还是说一般来说,你应该尽快取消所有引用?
- @Dispose模式建议使用空的大型对象;但是在.NET中,"大型"是一个模棱两可的术语。一般来说,它对大对象是有益的,更糟的是,你有一个额外的任务;负面副作用的说法不多。
- @皮特:我同意有些情况下这样做可能会有帮助。但是,操作人员特别询问如果引用不为空是否会导致内存泄漏。在这两种情况下都将收集对象。我不想在任何情况下都要记住这一点,因为它与自动垃圾收集的整个理念背道而驰。
- 在不实现Dispose Too;时收集对象。OP询问Dispose这一事实意味着他对"内存泄漏"的定义意味着尽可能快地收集托管对象,而将字段设置为空则会从父对象中取消对该对象的OOT,从而使GC能够更快地收集它,这意味着将字段设置为空将适用,imho。牛传染性胃肠炎病毒
- @皮特:可能是这样,但我肯定不是这样理解这个问题的。我同意,如果保存引用的实例比引用的对象长得多,则空的引用对于字段是有意义的。但是,由于我们讨论的是Dispose(),我不认为在调用Dispose()之后会发生这种情况,因此,空的引用可能几乎没有效果或没有效果。
- @将值设置为空的大脑将始终没有可测量的不利影响。事实证明,有时将值设置为空有好处,有时则没有。bit.ly/itk9n2与大多数情况一样:通过测量找出它是否会对您的环境产生影响。
- @彼得里奇:我同意,而且我只建议在非常具体的情况下采用这种做法。
- 如果是低级别的对象(即否则将其设置为空以释放它。如果你在一个超级计算机上工作,那么就要马虎,但是在像移动这样的有限资源上,这个答案显然是错误的。当处理图像时,GC的速度永远不够快。
- @尼克特纳请再看一遍我的答案。我不是说你不应该打电话给Dispose。
型
只是没用而已。我相信,在旧的com/vb天设置为空将减少引用计数。
在.NET中不是这样的。当您将BAR设置为空时,不会破坏或释放任何内容。您只是将条指向的引用从对象更改为"空"。您的对象仍然存在(尽管现在没有引用它,但它最终将被垃圾收集)。除了少数例外,在大多数情况下,如果你一开始就不让foo idisposable的话,这也是会发生的事情。
IDisposable的主要目的是允许您释放非托管资源,如TCP套接字或SQL连接,或其他任何资源。这通常是通过调用非托管资源提供的任何清理函数来完成的,而不是通过将引用设置为"null"。
- 好吧,如果我有一个TCP套接字,那该怎么办?设置为空是否无效?因为它是通过参数传递的,"某人"可以使用这个对象…
- 是的,那是没用的。如果您有一个TCP套接字,您可以通过调用该套接字的.close()方法来释放它。同样的事情也适用于SQL连接。设置为"空"实际上只会更改对正在使用的对象的引用。
- -1,设置为"无"允许垃圾收集器在第一次通过时进行清理。
- @阿米西科:设置成什么都不做,而不是让它超出范围?只有当它在范围内,但长时间未使用时,这才是重要的。
- 当清理对象时,会发生超出范围的情况,而不是在调用Dispose时。因此,设置为"没有任何帮助",特别是对于短暂的物体,如奥罗诺特所指出的。
- @amissco:当引用超出范围时,就会出现超出范围的情况。例如,在局部变量的方法末尾。
- @约翰·桑德斯,你在杀我。o)
- @阿米西科:我很想回来说,"你不知道你在说什么"。但是相反,你可能想读一本关于CLR的好书来教育自己。我推荐Jeff Richter的优秀作品"通过C_的clr"。
型
在vb.net中,有必要设置为Nothing声明的Private WithEvents对象。
使用Handles关键字的处理程序将以这种方式从这些对象中删除。
型
在C中,将对象设置为空只是释放对该对象的引用。
因此,理论上最好在C中释放Dispose方法中对托管对象的引用,但仅限于GC在收集已释放对象之前收集被引用对象的可能性。由于这两个对象很可能在同一个运行中被收集,所以GC将最恰当地认识到,被引用的对象仅由已释放的类型引用,因此可以同时收集这两个对象。
另外,释放引用的需求也很小,因为如果类已经被释放,可释放类的所有公共成员都应该抛出一个异常。因此,在释放被引用的方法之后,对被引用对象的任何访问都不会成功。
- thx dave,更改了vb.net的信息
- 那么,当不设置任何内容时,C和vb.net之间的区别是什么?为了可读性,我在C中暴露了这个问题,但我真正的项目是在vb.net中。
- 就你的目的而言,没有区别。但vb.net是一种可怕的语言。在vb.net中,如果将dim x设置为integer=nothing,然后打印"x"的值,则得到0。在C中,它不编译,因为"in t"是一个值类型,"null"严格来说是一个引用。所以他们的行为不完全一样。但是对于IDisposable对象这样的引用类型,它们的行为完全相同。
型
如果您想以某种方式阻止重新使用释放的拥有的实例,这是有意义的。
当您将对可释放字段的引用设置为空时,将保证不再使用这些实例。
您不会得到ObjectDisposedException或任何其他由于使用所拥有的释放实例而导致的无效状态(如果不检查空值,您可以得到NullReferenceException)。
只要所有IDisposable对象都具有IsDisposed属性和/或如果在处置后使用ObjectDisposedException对象,这可能对您没有意义,有些对象可能违反此原则,将其设为空可能会防止产生不必要的影响。
型
通常不需要设置为空。但是假设您的类中有一个重置功能。
然后您可能会这样做,因为您不希望调用Dispose两次,因为某些Dispose可能无法正确实现,并引发System.ObjectDisposed异常。
1 2 3 4 5 6 7 8 9
| private void Reset()
{
if(_dataset != null)
{
_dataset.Dispose();
_dataset = null;
}
//..More such member variables like oracle connection etc. _oraConnection
} |
型
Dispose()的目的是清除未管理的资源。TCP连接、数据库连接和其他数据库对象以及许多此类非托管资源应该由开发人员在Dispose方法中释放。所以这很有道理。
- 对于使用gdi+位图和简单自定义.NET对象栏的两个示例?我不处理它们,因为传入的参数不是由对象创建的。