Создайте класс, который реализует проверку утечки памяти - PullRequest
0 голосов
/ 14 июня 2019

Я хотел бы обнаружить утечки памяти в моих приложениях. Я бы попытался найти утечки без установки программного обеспечения или расширений.

По этой ссылке

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

Мне кажется, что это хорошо работает, но, поскольку я глубоко понимаю, что сборщик мусора требует много времени, которого у меня сейчас нет, я бы спросил сообщество, может ли этот класс эффективно использоваться для поиска утечек памяти, поскольку я его построил. .

Ответы [ 2 ]

0 голосов
/ 21 июня 2019

Поскольку я не получил много ответов, я отвечаю сам.

Моя проблема заключалась в том, что у меня была программа, которая запускалась с использованием 25 МБ памяти, а со временем она достигла 1,5 ГБ, а затем потерпела крах с исключением «Недостаточно памяти».

Следующие советы у меня есть:

  • Я искал ПО 3-ей части : но ему нужно много времени, чтобы изучить, как оно работает.
  • Установленная Visual Studio Community 2019, которая интегрирует Visual Studio Diagnostic , но не дает немедленного и точного местоположения утечек.
  • Я изучал предыдущий ответ об утечках памяти в Интернете: я понял, что в общем утечки связаны с неправильной реализацией интерфейса iDisposable .

Через класс, который я опубликовал в примере, я легко и быстро нашел процедуры, которые вызывали утечки.

В рамках этих процедур я определил классы, необходимые для реализации iDisposable, и части кода, которые следует переписать.

В этот момент я решил в этом режиме:

  • Правильное внедрение iDisposable: краткое правильное объяснение здесь Реализация IDisposable

  • Оптимизация кода класса с использованием другого конструктора для создания экземпляров.

в частности, я заменил этот тип кода

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

с этим (NB. Не совсем уверен насчет кода для удаления экземпляров, у меня это, кажется, работает хорошо)

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
0 голосов
/ 14 июня 2019

Нахождение утечек мори нелегко.Вам следует пересмотреть установку ПО сторонних производителей, чтобы обнаружить их.Или используйте вкладку диагностики Visual Studio при отладке.

GC медленный, потому что он должен делать много проверок памяти.Этот класс просто скажет вам, могут ли быть некоторые утечки или нет.Но он не скажет вам, где их найти.

...