我注意到当我打开主窗口时,占用了1500K的内存,当我打开一个子窗口时,占用的内存增加到了6000K。
当我打开第二个子窗口时,同样的情况发生。但是当我关闭这两个子窗口时,占用的内存并没有被释放。
所以,我希望每当我关闭子窗口时都能释放占用的内存。
请给我一些vb.net代码示例来指导我如何做到这一点。
这个问题经常出现在局域网上的计算机上,而不是我的电脑(开发人员电脑上有SQL服务器)。
这个问题的其他答案中有很多错误信息,同时也有一些答案把问题过于复杂化了。在.NET中,垃圾回收存在许多误解,这里所谈论的理论肯定不会有所帮助。
首先,使用Windows任务管理器来分析内存使用是一个巨大的错误。你将得到严重的无效信息,试图根据此信息修改应用程序只会使情况变得更糟,而不是更好。如果你怀疑自己有性能问题(实际上大多数应用程序都不太可能遇到这种情况),那么你需要投资购买一个合适的内存分析器并使用它。
其次,垃圾回收的整个重点在于你不必担心这种事情。你不仅不需要担心它,而且你也不应该担心它。当编写针对.NET Framework的应用程序时,不应进行任何类型的手动内存管理。抵制诱惑去调整垃圾回收器的内部工作方式,并在有人告诉你手动调用GC.Collect
以强制进行垃圾回收时,要将手指牢固地插在耳朵里。我想我不应该说永远不要这样做,但几乎没有理由这样做。我更可能怀疑手动调用垃圾回收的代码,而不是其他任何东西。
为什么不应手动调用垃圾回收?好吧,除了显而易见的使用托管语言的整个目的被击败的论点之外,还因为垃圾回收是一项非常缓慢和昂贵的过程。你希望它尽可能地少运行,以保持最佳性能。幸运的是,实现垃圾回收算法的程序员比你或我聪明得多,更有经验:他们设计它只在必要时运行,并且不会更频繁地运行。你看不到更频繁运行它的优势,但是你会看到缺点。这对于程序员来说应该是完全不透明的。
唯一的例外是当您使用未受垃圾回收器管理或回收的非托管对象时。您可以通过它们实现IDisposable
接口来识别这些对象,该接口提供了一个Dispose
方法以释放非托管资源。对于公开此方法的对象,应在使用对象后立即调用它。或者更好的做法是,在using
语句中包装对象的声明和使用,这将自动处理对象的处理,无论发生什么情况(例如,在使用对象的代码中抛出异常)。IDisposable
方法。例如,无处不在的Form
类提供了一个Dispose
方法。但是,这并不一定意味着您需要手动处理这些对象的处理。通常情况下,您只需要显式调用Dispose
方法来处理您明确创建的对象 - 易于记忆,对吧?Framework自动创建的对象也会被Framework自动销毁。例如,在设计时放置在Form
对象上的控件在其容器窗体关闭时会自动释放。而Form
对象本身在关闭时也会自动释放。这与您提出的问题特别相关。Form.Close
方法的文档告诉我们:
请注意,通常情况下,您不必从您的代码中手动调用当一个窗体关闭时,所有在该对象内部创建的资源都会关闭,并且该窗体会被释放。
[...]
当窗体是多文档界面(MDI)应用程序的一部分且窗体不可见时,以及您使用
ShowDialog
显示窗体时,窗体不会在Close
时释放。在这些情况下,您需要手动调用Dispose
来标记窗体的所有控件进行垃圾回收。
Form.Dispose
。当MDI父窗体不可见时,用户无法关闭MDI子窗体,如果在其父窗体不可见时您自己在代码中关闭了该窗体,则可以简单地插入一个调用Form.Dispose
的语句。使用ShowDialog
方法将表单显示为模态对话框时,可以方便地在using
语句中包装其创建和使用。Dispose
方法只会释放非托管资源,并标记该对象可供垃圾回收。这并不会立即释放该对象所占用的内存。这很重要,因为这正是您进行内存分析的关注点所在。您知道对象已被处理,因为您提到变量变得不可用(您说您“失去了它们的值”)。那是因为您无法访问已处理的对象。然而,这并不一定意味着它们所占用的内存已完全释放。这是垃圾回收器的工作,我们已经确定您不应该随意干涉它。它将等待释放内存,直到应用程序处于空闲状态或者它迫切需要重新使用该内存。否则,它会推迟回收,这仍然是可以的。using
语句来实现,这是更好的方法),否则打开的文件在垃圾回收运行之前将无法使用。这样的非内存对象不一定是非托管的(例如,数据库连接可以完全由托管代码实现)。 - RichardIDisposable
模式,我在我的答案中已经考虑到了这一点。而且,使用using
语句是正确的方法,而不是任何类型的手动内存管理(比如自己调用垃圾回收器)。我可能本可以再多说一点,但我开始非常怀疑是否有人会一直读到最后... - Cody GrayIDisposable
(这就是为什么我使用了“生命周期”)。但我的评论针对的是您的段落开头:“唯一的例外”刚刚说让系统自己处理它。解释这个问题的问题在于答案必须说“让GC自己做自己的事情,除非”,而且“当”需要涵盖:语法上作用域的 IDisposable
,其他 IDisposable
对象(例如,在字段中)和直接使用未托管的对象(因此是终结器的主题);尽管我可能只是简单地提及最后一个“高级”问题。 - Richard利用使用块,自动释放内存
Using { resourcelist | resourceexpression }
[ statements ]
End Using
GC.Collect
确实是手动调用垃圾回收的方法,但是周围没有足够明显的警告文字来说明这种建议的风险。我可以保证,调用GC.Collect
不是解决方案。 - Cody Gray按照Pranay的建议使用将起作用,因为它会默认调用Dispose方法。否则,在调用子窗体的this.close()之后,您必须显式地调用this.dispose()。但请确保在关闭后不再使用子窗体元素或值,因为dispose最终会清除所有内容。
MSDN处有释放非托管资源的示例
Imports System
Imports System.ComponentModel
' The following example demonstrates how to create
' a resource class that implements the IDisposable interface
' and the IDisposable.Dispose method.
Public Class DisposeExample
' A class that implements IDisposable.
' By implementing IDisposable, you are announcing that
' instances of this type allocate scarce resources.
Public Class MyResource
Implements IDisposable
' Pointer to an external unmanaged resource.
Private handle As IntPtr
' Other managed resource this class uses.
Private component As component
' Track whether Dispose has been called.
Private disposed As Boolean = False
' The class constructor.
Public Sub New(ByVal handle As IntPtr)
Me.handle = handle
End Sub
' Implement IDisposable.
' Do not make this method virtual.
' A derived class should not be able to override this method.
Public Overloads Sub Dispose() Implements IDisposable.Dispose
Dispose(True)
' This object will be cleaned up by the Dispose method.
' Therefore, you should call GC.SupressFinalize to
' take this object off the finalization queue
' and prevent finalization code for this object
' from executing a second time.
GC.SuppressFinalize(Me)
End Sub
' Dispose(bool disposing) executes in two distinct scenarios.
' If disposing equals true, the method has been called directly
' or indirectly by a user's code. Managed and unmanaged resources
' can be disposed.
' If disposing equals false, the method has been called by the
' runtime from inside the finalizer and you should not reference
' other objects. Only unmanaged resources can be disposed.
Protected Overridable Overloads Sub Dispose(ByVal disposing As Boolean)
' Check to see if Dispose has already been called.
If Not Me.disposed Then
' If disposing equals true, dispose all managed
' and unmanaged resources.
If disposing Then
' Dispose managed resources.
component.Dispose()
End If
' Call the appropriate methods to clean up
' unmanaged resources here.
' If disposing is false,
' only the following code is executed.
CloseHandle(handle)
handle = IntPtr.Zero
' Note disposing has been done.
disposed = True
End If
End Sub
' Use interop to call the method necessary
' to clean up the unmanaged resource.
<System.Runtime.InteropServices.DllImport("Kernel32")> _
Private Shared Function CloseHandle(ByVal handle As IntPtr) As [Boolean]
End Function
' This finalizer will run only if the Dispose method
' does not get called.
' It gives your base class the opportunity to finalize.
' Do not provide finalize methods in types derived from this class.
Protected Overrides Sub Finalize()
' Do not re-create Dispose clean-up code here.
' Calling Dispose(false) is optimal in terms of
' readability and maintainability.
Dispose(False)
MyBase.Finalize()
End Sub
End Class
Public Shared Sub Main()
' Insert code here to create
' and use the MyResource object.
End Sub
End Class
(更新)[检查]
如果您的子表单有签名。这些默认情况下会添加到表单中。
'Form overrides dispose to clean up the component list.
Protected Overloads Overrides Sub Dispose(ByVal disposing As Boolean)
If disposing Then
If Not (components Is Nothing) Then
components.Dispose()
End If
End If
MyBase.Dispose(disposing)
End Sub
您可以使用其中一个可用的内存分析器,例如ANTS Memory Profiler(请参阅在WinForms应用程序中使用ANTS Memory Profiler跟踪内存泄漏)。您也可以使用WinDbg,但如果没有经验,则比专业工具更复杂。
常见的“内存泄漏”原因之一是将“外部”事件处理程序添加到窗体(例如静态或长期存在的对象)中,并在窗体销毁时不予删除,因此GC“认为”您对窗体有引用并且不收集其数据。
垃圾回收器会为你处理这些工作,所以你真的不需要担心它。只有在使用非托管资源(com interop/PInvoke)时,你才需要更深入地了解其中的情况。
我不是VB.net的专家,但据我所知它有一个垃圾回收器。这通常意味着关闭子窗口并不会释放内存,但如果您删除所有对子窗口的引用,垃圾回收器可能会在下一次运行时释放它。
另外,通常可以“请求”垃圾回收器运行,但它通常会自行“决定”何时运行。