Suchen, beheben und verhindern Sie Speicherlecks in C # .NET: 8 Best Practices

Für zukünftige Studenten des "C # Developer" -Kurses und alle Interessierten haben wir eine Übersetzung von nützlichem Material vorbereitet.



Wir laden Sie außerdem ein, an dem
offenen Webinar zum Thema "LINQ-Methoden, die alles für Sie tun" teilzunehmen. Dort diskutieren die Teilnehmer sechs Vertreter der LINQ-Technologiefamilie, drei Komponenten der Hauptabfrageoperation, verzögerte und sofortige Ausführung sowie parallele Abfragen.






Jeder, der an einem großen Unternehmensprojekt gearbeitet hat, weiß, dass Speicherlecks wie Ratten in einem großen Hotel sind. Sie werden sie vielleicht nicht bemerken, wenn es nur wenige gibt, aber Sie sollten immer auf der Hut sein, falls sie sich vermehren, sich in die Küche schleichen und alles verschmutzen.





, — . 8 , .NET , . , , . , .





.NET

« » . , (GC garbage collector), ?





. — , , . , , , . , , event



.





, - ( ) . . .NET , . , , , , . Dispose



, ( ). .NET ., Marshal



PInvoke



( ).





:





1.

Debug | Windows | Show Diagnostic Tools, . -, , , Visual Studio, . . 2 : GC Pressure ( ).





, (Process Memory) :





, , , , - .





GC Pressure, :





GC Pressure — , . , , .





, , , . Visual Studio Enterprise , . .





2. , Process Explorer PerfMon

(Task Manager) Process Explorer ( SysInternals). , . , , .





PerfMon , . , , . Process | Private Bytes.





, . , . , / , (). , GC Pressure. , , .





, , . , - ( ).





3.

-. . ( ), , .





.NET: dotMemory, SciTech Memory Profiler ANTS Memory Profiler. «» , Visual Studio Enterprise.





. . . . , :





, , GC Root.





GC Root — , , , GC Root, . , , GC Roots. « .NET».





— , . , . , :





  1. - (Idle state) . - .





  2. , .





  3. , , . .





  4. .





  5. .





  6. New-Created-Instances, , . «path to GC Root» , .





, SciTech , :





4. «Make Object ID»

5 , - C# .NET, , , Finalizer. , . Make Object ID (Immediate Window).





, , . , , . , , :





  1. , .





  2. , , Make Object ID



    . Immediate $1



    , , Object ID



    .





  3. , .





  4. .





GC.Collect();
GC.WaitForPendingFinalizers();
GC.Collect();
      
      



5. $1



. null



, , . , .





:





, :





, , .





: .NET Core 2.X (). , , . , , .





5.

, , . , .





:





  • (Events) .NET , . , , . , : 5 , - C# .NET,





  • , , , . , GC Roots, .





  • — . , OutOfMemory. .





  • WPF . — DependencyObject INotifyPropertyChanged. , WPF ( ViewModel) , . WPF StackOverflow.





  • . , , , — . :





public class MyClass
{
    private int _wiFiChangesCounter = 0;
 
    public MyClass(WiFiManager wiFiManager)
    {
        wiFiManager.WiFiSignalChanged += (s, e) => _wiFiChangesCounter++;
    }
      
      



  • , . Live Stack GC Root. , , , . . , . :





public class MyClass
{
    public MyClass(WiFiManager wiFiManager)
    {
        Timer timer = new Timer(HandleTick);
        timer.Change(TimeSpan.FromSeconds(5), TimeSpan.FromSeconds(5));
    }
 
    private void HandleTick(object state)
    {
        // do something
    }
      
      



8 .NET.





6. Dispose

.NET . .NET , Win32 API. , , , , , , .





.NET Framework



, , IDisposable



. , , Dispose



. — Dispose



. , using



.





public void Foo()
{
    using (var stream = new FileStream(@"C:\Temp\SomeFile.txt",
                                       FileMode.OpenOrCreate))
    {
        // do stuff
 
    }// stream.Dispose() will be called even if an exception occurs
      
      



using



try / finally



, Dispose



finally



.





Dispose



, , .NET



Dispose. , Dispose



, Finalizer



, . , Finalizer



.





, Dispose



. :





public class MyClass : IDisposable
{
    private IntPtr _bufferPtr;
    public int BUFFER_SIZE = 1024 * 1024; // 1 MB
    private bool _disposed = false;
 
    public MyClass()
    {
        _bufferPtr =  Marshal.AllocHGlobal(BUFFER_SIZE);
    }
 
    protected virtual void Dispose(bool disposing)
    {
        if (_disposed)
            return;
 
        if (disposing)
        {
            // Free any other managed objects here.
        }
 
        // Free any unmanaged objects here.
        Marshal.FreeHGlobal(_bufferPtr);
        _disposed = true;
    }
 
    public void Dispose()
    {
        Dispose(true);
        GC.SuppressFinalize(this);
    }
 
    ~MyClass()
    {
        Dispose(false);
    }
}

      
      



— . , ( Finalizer



), Dispose()



.





GC.SuppressFinalize(this)



. , Finalizer



, . Finalizer- . Finalizer



F-Reachable-Queue



, . .





7.

. , , . , - , . , , .





. :





Process currentProc = Process.GetCurrentProcess();
var bytesInUse = currentProc.PrivateMemorySize64;
      
      



PerformanceCounter



— , PerfMon



:





PerformanceCounter ctr1 = new PerformanceCounter("Process", "Private Bytes", Process.GetCurrentProcess().ProcessName);
PerformanceCounter ctr2 = new PerformanceCounter(".NET CLR Memory", "# Gen 0 Collections", Process.GetCurrentProcess().ProcessName);
PerformanceCounter ctr3 = new PerformanceCounter(".NET CLR Memory", "# Gen 1 Collections", Process.GetCurrentProcess().ProcessName);
PerformanceCounter ctr4 = new PerformanceCounter(".NET CLR Memory", "# Gen 2 Collections", Process.GetCurrentProcess().ProcessName);
PerformanceCounter ctr5 = new PerformanceCounter(".NET CLR Memory", "Gen 0 heap size", Process.GetCurrentProcess().ProcessName);
//...
Debug.WriteLine("ctr1 = " + ctr1 .NextValue());
Debug.WriteLine("ctr2 = " + ctr2 .NextValue());
Debug.WriteLine("ctr3 = " + ctr3 .NextValue());
Debug.WriteLine("ctr4 = " + ctr4 .NextValue());
Debug.WriteLine("ctr5 = " + ctr5 .NextValue());
      
      



perfMon, .





. CLR MD (Microsoft.Diagnostics.Runtime) . , , , . .





, CLR MD, DumpMiner .





, , , Application Insights.





8.  

— . . , :





[Test]
void MemoryLeakTest()
{
  var weakRef = new WeakReference(leakyObject)
  // Ryn an operation with leakyObject
  GC.Collect();
  GC.WaitForPendingFinalizers();
  GC.Collect();
  Assert.IsFalse(weakRef.IsAlive);
}
      
      



, .NET Memory Profiler SciTech dotMemory, API:





MemAssertion.NoInstances(typeof(MyLeakyClass));
MemAssertion.NoNewInstances(typeof(MyLeakyClass), lastSnapshot);
MemAssertion.MaxNewInstances(typeof(Bitmap), 10);
      
      



, , , , : .





, , . .






« C#».









« LINQ, ».












All Articles