Rechercher, corriger et prévenir les fuites de mémoire dans C # .NET: 8 meilleures pratiques

Pour les futurs étudiants du cours "C # Developer" et tous ceux qui sont intéressés, nous avons préparé une traduction de matériel utile.



Nous vous invitons également à participer au
webinaire ouvert sur le thème «Les méthodes LINQ qui font tout pour vous» - où les participants discuteront de six représentants de la famille de technologies LINQ, de trois composants de l'opération de requête principale, de l'exécution différée et immédiate, des requêtes parallèles.






Quiconque a travaillé sur un grand projet d'entreprise sait que les fuites de mémoire sont comme des rats dans un grand hôtel. Vous ne les remarquerez peut-être pas quand ils sont peu nombreux, mais vous devriez toujours être à l'affût au cas où ils se reproduiraient, se faufiler dans la cuisine et salir tout autour.





, — . 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