前几天,我尝试着找到一些方法来消除我的应用程序中的一些内存泄漏问题,但是我意识到我几乎不知道如何清理资源。我进行了一些研究,希望只调用 .dispose() 方法就能解决所有问题。我们的数据库中有一个包含大约 65,000 条记录的表格。显然,当我从数据适配器填充数据集时,内存使用量可能会非常高。当我在数据集上调用 dispose 方法时,惊讶地发现没有任何内存被释放。为什么会这样?清除数据集也没有帮助。
IDisposable和Dispose不是用于减少内存压力,尽管在某些情况下可能会这样做,而是用于进行确定性清理。
考虑如下情况:您构造了一个对象,该对象维护与数据库服务器的活动和开放连接。此连接使用计算机和服务器上的资源。
当您完成对该对象的使用时,您可以将其留给垃圾回收器自行处理,但是如果您希望确保至少释放资源并关闭连接,则需要使用IDisposable.Dispose。
它用于清理对象管理的资源。
然而,它不会释放分配给对象的托管内存。这仍然由垃圾回收器留待稍后处理。
您实际上是否有内存问题,或者您只是查看任务管理器或类似工具中的内存使用情况,并说“有点高”?
如果是后者,则现在应该将其放置不管。如果可用内存较少,则.NET会更频繁地运行垃圾回收,因此,除非您处于可能很快发生内存溢出的情况,否则您可能不会遇到任何问题。
让我解释一下“较少运行”的含义。
如果你的机器上有8GB的内存,只运行Windows和记事本,那么大部分内存将可用。现在,当你运行程序时,即使它把小的数据块加载到内存中,你可以持续这样做很长一段时间,并且内存使用量会不断增长。准确地说,GC何时开始尝试降低内存占用我不知道,但我几乎可以保证你会想知道为什么内存使用量如此之高。
为了论证而言,假设你的程序最终将使用2GB的内存。
现在,如果在可用内存较少的机器上运行程序,则GC会更频繁地发生,并且会在较低的限制下开始,这可能会使内存使用量保持在500MB或甚至更低。
要注意的重要部分是,为了准确了解应用程序实际所需的内存量,不能依赖于任务管理器或类似的测量方式,需要使用更具针对性的工具。
调用Dispose()方法将只释放非托管资源,例如文件句柄、数据库连接、非托管内存等。 它不会释放垃圾回收的内存。
垃圾回收的内存只有在下一次回收时才会被释放。通常是当应用程序域内存被认为已满时。
Dispose()
才会起作用。Dispose()
是无法解决它的,如果原始开发人员做得很差,没有正确释放非托管资源。更多信息,请参见此博客文章。请注意语句 Dispose 的行为由开发人员定义。
有些对象会要求一个或多个其他实体代表它做某事,直到另行通知,这会对其他实体造成损害。如果这样做的对象消失而没有告知前面的实体他们的服务不再需要,那么这些实体将继续无用地代表一个不再需要他们的对象行动,从而继续损害其他想要使用它们的实体。
在许多情况下,为了告诉外部实体“Joe”它的服务不再需要,对象“George”必须知道它的服务不再需要。在.NET中,有两种正常的方法可以实现这一点,即Finalization和IDIsposable。
如果一个对象重写了一个名为Finalize的方法,那么当创建该对象时,.NET垃圾收集器将把它添加到具有已注册终结器的对象列表中。如果GC发现除了该列表之外不存在根引用到该对象,则GC将从该列表中删除该对象,并将其添加到一个强根队列中,这些对象应尽快调用其Finalize方法。这样的对象可以使用其Finalize方法来告知其他实体他们的服务不再需要。
尽管基于终结的清理有时可能有效,但不能保证及时性。在 .net 的设计过程中,微软曾经可能打算让终结成为主要的清理方法,但由于各种原因,它不能被可靠地依赖。IDisposable
。基本上,IDisposable
的思想很简单:对于每个实现了 IDisposable
的对象,应该有一个实体(通常是对象或嵌套执行范围之一)负责确保该对象的 IDisposable.Dispose
方法会在宇宙的生命周期内的某个时间被调用(这意味着在仍然存在对象的引用时),并且最好在代码可以确定不再需要该对象的服务时立即调用。IDisposable.Dispose
通常承诺告知任何已被要求代表对象执行某些操作的外部实体,他们不再需要这样做,但这样的承诺并不意味着实体的数量为零。如果一个对象没有要求任何外部实体代表它执行任何操作,则传递“所有”这些实体的消息根本不需要执行任何操作。另一方面,Dispose
方法在某些情况下可能什么也不做,并不意味着它从来不会在任何情况下执行任何操作,也不意味着在那些它会执行某些操作的情况下不调用它不会产生不利影响。