生成实现内存泄漏检查的类



我想在我的应用程序中查找内存泄漏。 我会尝试在没有安装软件或扩展的情况下找到泄漏。

点击此链接

https://michaelscodingspot.com/5-techniques-to-avoid-memory-leaks-by-events-in-c-net-you-should-know/

我试图建立自己的班级。

这里的代码:

''' <summary>
''' Use: lanch instance of the class
''' - Friend memorytest As New MemoryLeakTest(MemoryLeakTest.memorySize.MBytes)
''' Parameters:
''' - MemorySize (MBytes, KBytes, Bytes)
''' - AlertSize (set Alert to True if the increment of the new memory usage it's >)
''' 
''' And then, to record the memory usage of a step
''' - memorytest.NewStep("abc")
''' 
''' To record and get the memory usage of every step:
''' - PrintMethod(memorytest.NewStep("abc").ToString)
''' 
''' To record a step and get only results with an Increment of the Memory used > initial alertSize setting:
''' - If memoryTest.NewStepConditioned("abc") Then PrintMethod(memoryTest.LastStep)
''' 
''' To get the sum of all Memory increments by step:
''' - PrintMethod(memorytest.GetMemoryUsedByStep("abc"))
''' </summary>
''' <remarks></remarks>
Friend Class MemoryLeakTest
Sub New(Optional ByVal newMemorySize As memorySize = memorySize.KBytes, Optional ByVal newAlertSize As Integer = 0)
_memorySize = newMemorySize
_alertSize = newAlertSize
End Sub
Private _memorySize As memorySize
Private _alertSize As Integer
Private _Priority As Integer
Private _Id As Integer
Private _Title As String
Private _MaxWorkingSet As IntPtr
Private _MinWorkingSet As IntPtr
Private _ProcessName As String
Private _StartInfo As System.Diagnostics.ProcessStartInfo
Private _PriorityClass As System.Diagnostics.ProcessPriorityClass
Private _MemorySteps As New List(Of MemoryStep)
Friend Function NewStep(ByVal _Step As String) As String
CallGC()
CheckMemoryStep(_Step)
'CallGC()
Return _MemorySteps(_MemorySteps.Count - 1).ToString
End Function
Friend Function NewStepConditioned(ByVal _Step As String) As Boolean
CallGC()
CheckMemoryStep(_Step)
'CallGC()
Return _MemorySteps(_MemorySteps.Count - 1).Alert
End Function
Friend Function LastStep() As String
Return _MemorySteps(_MemorySteps.Count - 1).ToString
End Function
Friend Function GetMemoryUsedByStep(ByVal NewStep As String) As String
Dim MemoryUsed As List(Of MemoryStep) = _MemorySteps.FindAll(Function(x) x._step = NewStep)
Return MemoryUsed.Sum(Function(x) x._inc_WorkingSet).ToString & " " & _memorySize.ToString
End Function
Friend Sub CallGC()
GC.Collect()
GC.WaitForPendingFinalizers()
'GC.Collect()
End Sub
Friend Sub CheckMemoryStep(ByVal newStep As String)
Using cp As Process = Process.GetCurrentProcess
If IsNothing(_StartInfo) Then
_Priority = cp.BasePriority
_Id = cp.Id
_Title = cp.MainWindowTitle
_MaxWorkingSet = cp.MaxWorkingSet
_MinWorkingSet = cp.MinWorkingSet
_PriorityClass = cp.PriorityClass
_ProcessName = cp.ProcessName
_StartInfo = cp.StartInfo
_MemorySteps.Add(New MemoryStep(newStep, cp, _memorySize, _alertSize))
Else
_MemorySteps.Add(New MemoryStep(newStep, cp, _memorySize, _alertSize, _MemorySteps(_MemorySteps.Count - 1)))
End If
End Using
End Sub
Friend Class MemoryStep
Sub New(ByVal newStep As String, ByVal cp As System.Diagnostics.Process, ByVal memSize As memorySize, ByVal alertSize As Integer, Optional ByVal oldStep As MemoryStep = Nothing)
_memorySize = memSize
_step = newStep
_NonPagedSystemMemory = CLng(cp.NonpagedSystemMemorySize64 / memSize)
_PagedMemory = CLng(cp.PagedMemorySize64 / memSize)
_PagedSystemMemory = CLng(cp.PagedSystemMemorySize64 / memSize)
_PeakPagedMemory = CLng(cp.PeakPagedMemorySize64 / memSize)
_PeakVirtualMemory = CLng(cp.PeakVirtualMemorySize64 / memSize)
_PeakWorkingSet = CLng(cp.PeakWorkingSet64 / memSize)
_PrivateMemory = CLng(cp.PrivateMemorySize64 / memSize)
_VirtualMemory = CLng(cp.VirtualMemorySize64 / memSize)
_WorkingSet = CLng(cp.WorkingSet64 / memSize)
If Not IsNothing(oldStep) Then
With oldStep
_inc_NonPagedSystemMemory = _NonPagedSystemMemory - ._NonPagedSystemMemory
_inc_PagedMemory = _PagedMemory - ._PagedMemory
_inc_PagedSystemMemory = _PagedSystemMemory - ._PagedSystemMemory
_inc_PeakPagedMemory = _PeakPagedMemory - ._PeakPagedMemory
_inc_PeakVirtualMemory = _PeakVirtualMemory - ._inc_PeakVirtualMemory
_inc_PeakWorkingSet = _PeakWorkingSet - ._PeakWorkingSet
_inc_PrivateMemory = _PrivateMemory - ._PrivateMemory
_inc_VirtualMemory = _VirtualMemory - ._VirtualMemory
_inc_WorkingSet = _WorkingSet - ._WorkingSet
End With
End If
If _inc_WorkingSet > alertSize Then
Alert = True
End If
End Sub
Private _NonPagedSystemMemory As Long
Private _PagedMemory As Long
Private _PagedSystemMemory As Long
Private _PeakPagedMemory As Long
Private _PeakVirtualMemory As Long
Private _PeakWorkingSet As Long
Private _PrivateMemory As Long
Private _VirtualMemory As Long
Private _WorkingSet As Long
Private _memorySize As memorySize
Private _inc_NonPagedSystemMemory As Long
Private _inc_PagedMemory As Long
Private _inc_PagedSystemMemory As Long
Private _inc_PeakPagedMemory As Long
Private _inc_PeakVirtualMemory As Long
Private _inc_PeakWorkingSet As Long
Private _inc_PrivateMemory As Long
Private _inc_VirtualMemory As Long
Friend _inc_WorkingSet As Long
Friend _step As String
Friend Alert As Boolean = False
Public Overrides Function ToString() As String
ToString = _step & vbTab & "memory usage: " & _WorkingSet.ToString & " " & _memorySize.ToString
If _inc_WorkingSet < 0 Then
ToString += " (- " & -_inc_WorkingSet.ToString & " " & _memorySize.ToString & ")"
Else
ToString += " (+ " & _inc_WorkingSet.ToString & " " & _memorySize.ToString & ")"
End If
Return ToString
End Function
End Class
Enum memorySize
MBytes = 1024000
KBytes = 1024
[Bytes] = 1
End Enum
End Class

此类的用法应为:

Class Test
Friend memorytest As New MemoryLeakTest(MemoryLeakTest.memorySize.MBytes)
Sub RunTest()
memorytest.NewStep("Start")
Method_1()
WriteToTextBox(memorytest.NewStep("Method_1").ToString)
Method_2()
WriteToTextBox(memorytest.NewStep("Method_2").ToString)
Method_3()
If memoryTest.NewStepConditioned("Method_3") Then WriteToTextBox(memoryTest.LastStep)
End Sub
Private Sub Method_1()
'Do something
End Sub
Private Sub Method_2()
'Do something
End Sub
Private Function Method_3()
'Do something
End Function
Private Sub WriteToTextBox(ByVal msg As String)
With TextBox1
.SelectionStart = .Text.Length
.SelectedText = VbCrlf & Date.Now.ToLongTimeString & Chr(9) & msg
End With
End Sub
End Class

在我看来,这很好用,但是因为深入了解 GC 需要大量我目前没有的时间,我会问社区这个类是否可以有效地用于查找内存泄漏,因为我构建了它。

找到莫里泄漏并不容易。您应该重新考虑安装第三方软件来检测它们。或者在调试时使用 Visual Studio 诊断选项卡。

GC 很慢,因为它必须执行大量内存检查。这个类只会告诉你是否有一些死亡泄漏。但它不会告诉你在哪里可以找到它们。

由于我没有得到很多答案,所以我自己回答。

我的问题是我有一个程序,它以 25 MB 的内存使用量开始,随着时间的推移它达到 1.5 GB,然后因"内存不足异常"而崩溃。

我有以下建议:

  • 我搜索了第三部分软件:但它需要大量时间来研究它是如何工作的。
  • 已安装Visual Studio Community 2019,它集成了Visual 工作室诊断,但不能给出即时和准确的位置 的泄漏。
  • 研究了之前关于互联网内存泄漏的答案:我意识到一般来说,泄漏是由于iDisposable 接口的不正确实现造成的。

通过我在示例中发布的类,我可以轻松快速地找到产生泄漏的过程。

在这些过程中,我确定了实现 iDisposable 所需的类,以及应该重写的代码部分。

在这一点上,我在这个模式下解决了:

  • 正确实施一次性:在这里进行简短的正确解释 实现 ID是可置的

  • 优化类代码,使用不同的构造函数来实例化实例。

特别是,我替换了这种类型的代码

Class MyClass()
Sub New()
End Sub
Friend Inst_of_my_2nd_class As New My2ndClass()
Friend Inst_of_my_3rd_class As New My3rdClass()
Friend Inst_of_my_4th_class As New My4thClass()
End Class

有了这个(注意。不确定要处理实例的代码,对我来说似乎效果很好)

Class MyClass()
Implements iDisposable
Sub New(ByVal ... As some)
Inst_of_my_2nd_class = New My2ndClass()
End Sub
Sub New(ByVal ... As someother)
Inst_of_my_3rd_class = New My3rdClass()
End Sub
Sub New(ByVal ... As someotherelse)
Inst_of_my_4th_class = New My4thClass()
End Sub
Friend Inst_of_my_2nd_class As My2ndClass()
Friend Inst_of_my_3rd_class As My3rdClass()
Friend Inst_of_my_4th_class As My4thClass()
#Region "IDisposable Support"
Protected Overridable Sub Dispose(disposing As Boolean)
If Not Me.disposedValue Then
If disposing Then
' TODO: dispose managed state (managed objects).
' what to put here: thanks to https://stackoverflow.com/questions/19895856/implementing-idisposable/19896116#19896116
End If
' TODO: free unmanaged resources (unmanaged objects) and override Finalize() below.
' what to put here: thanks to https://stackoverflow.com/questions/19895856/implementing-idisposable/19896116#19896116
If Not IsNothing(Inst_of_my_2nd_class) Then
Inst_of_my_2nd_class.Dispose()
Inst_of_my_2nd_class = Nothing
End If
If Not IsNothing(Inst_of_my_3rd_class) Then
Inst_of_my_3rd_class.Dispose()
Inst_of_my_3rd_class = Nothing
End If
If Not IsNothing(Inst_of_my_4th_class) Then
Inst_of_my_4th_class.Dispose()
Inst_of_my_4th_class = Nothing
End If
' TODO: set large fields to null.
' what to put here: thanks to https://stackoverflow.com/questions/19895856/implementing-idisposable/19896116#19896116
End If
Me.disposedValue = True
End Sub
' TODO: override Finalize() only if Dispose(ByVal disposing As Boolean) above has code to free unmanaged resources.
'Protected Overrides Sub Finalize()
'   ' Do not change this code.  Put cleanup code in Dispose(ByVal disposing As Boolean) above.
'    Dispose(False)
'    MyBase.Finalize()
'End Sub
' This code added by Visual Basic to correctly implement the disposable pattern.
Public Sub Dispose() Implements IDisposable.Dispose
' Do not change this code.  Put cleanup code in Dispose(disposing As Boolean) above.
Dispose(True)
GC.SuppressFinalize(Me)
End Sub
#End Region
End Class

最新更新