Temukan, perbaiki, dan cegah kebocoran memori di C # .NET: 8 praktik terbaik

Untuk calon siswa kursus "Pengembang C #" dan semua yang tertarik, kami menyiapkan terjemahan materi yang bermanfaat.



Kami juga mengundang Anda untuk berpartisipasi dalam
webinar terbuka dengan topik "Metode LINQ yang Melakukan Segalanya Untuk Anda" - di mana peserta akan membahas enam perwakilan dari kelompok teknologi LINQ, tiga komponen operasi kueri utama, eksekusi yang ditangguhkan dan segera, kueri paralel.






Siapapun yang pernah mengerjakan proyek perusahaan besar tahu bahwa kebocoran memori seperti tikus di hotel besar. Anda mungkin tidak menyadarinya jika jumlahnya sedikit, tetapi Anda harus selalu waspada jika mereka berkembang biak, menyelinap ke dapur dan mengotori segala sesuatu di sekitarnya.





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