程序師世界是廣大編程愛好者互助、分享、學習的平台,程序師世界有你更精彩!
首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫知識
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
 程式師世界 >> 編程語言 >> .NET網頁編程 >> 關於.NET >> .NET陷阱 三 “正確”使用控件也會造成內存洩露

.NET陷阱 三 “正確”使用控件也會造成內存洩露

編輯:關於.NET

在我們的代碼中,有時會在控件中添加對數據對象的引用。比如使用樹節點的Tag屬性保存相應的對象,以便在界面操作中能簡單的進行訪問。因為其它地方不會引用這些數據,所以我們期望在控件被銷毀時,垃圾回收機制能回收相應的內存。但當軟件運行了一段時間後,內存使用量會變得非常大。下面是簡化後的示例代碼:

using System;   
 using System.Windows.Forms;   
        
 namespace MemoryLeak   
 {   
     public class MainForm : Form   
     {   
         private Button holderButton;   
         private Button controlButton;   
         private FlowLayoutPanel panel;   
         private object checkGc;   
        
         public MainForm()   
         {   
             DumpMemoryUsage("before allocate checkGc.");   
             checkGc = MakeLargeObject();   
             DumpMemoryUsage("after allocate checkGc.");   
        
             holderButton = new Button();   
             holderButton.Enabled = false;   
             holderButton.AutoSize = true;   
             holderButton.Text = "The button holds large object.";   
             DumpMemoryUsage("before allocate holderButton.Tag.");   
             holderButton.Tag = MakeLargeObject();   
             DumpMemoryUsage("after allocate holderButton.Tag.");   
        
             controlButton = new Button();   
             controlButton.AutoSize = true;   
             controlButton.Text = "The button controls holderButton.";   
             controlButton.Click += (sender, e) =>   
             {   
                 DumpMemoryUsage("before release checkGc and holderButton.Tag.");   
                 panel.Controls.Remove(holderButton);   
                 holderButton.Dispose();   
                 holderButton = null;   
        
                 checkGc = null;   
                 DumpMemoryUsage("after release checkGc and holderButton.Tag.");   
             };   
        
             panel = new FlowLayoutPanel();   
             panel.AutoSize = true;   
             panel.FlowDirection = FlowDirection.TopDown;   
             panel.Controls.Add(controlButton);   
             panel.Controls.Add(holderButton);   
        
             Controls.Add(panel);   
         }       
        
         private void DumpMemoryUsage(string msg)   
         {   
             GC.Collect();   
             Console.WriteLine(msg);   
             Console.WriteLine(GC.GetTotalMemory(true));   
         }   
        
         private object MakeLargeObject()   
         {   
             var largeObject = new object[100];   
             for (int i = 0; i < largeObject.Length; ++i)   
             {   
                 var array = new int[100][];   
                 largeObject[i] = array;   
                 for (int j = 0; j < array.Length; ++j)   
                 {   
                     array[j] = new int[100];   
                 }   
             }   
        
             return largeObject;   
         }   
     }   
        
     static class Program   
     {   
         static void Main()   
         {   
             Application.Run(new MainForm());   
         }   
     }   
 }

代碼中的checkGc變量是為了在輸出中確認垃圾回收已經進行了。下面是輸出結果:

before allocate checkGc.   
 281576   
 after allocate checkGc.   
 4605632   
 before allocate holderButton.Tag.   
 4606384   
 after allocate holderButton.Tag.   
 8930480   
 before release checkGc and holderButton.Tag.   
 8940016   
 after release checkGc and holderButton.Tag.   
 4616824

由第4行的輸出可以看出,代碼中創建的每個大對象占用了大約4M的內存。問題在於,我們在代碼的第32-38行中已經將holderButter從panel中移除,調用了其Dispose方法,將其設置為null,另外也將checkGc設置為null。但第12的的輸出卻表明,只有一個大對象被回收了!為了找出問題所在,我使用ANTS Memory Profier查看了相應的內存使用情況,如下圖所示:

從中可以看出,確實有一個對象沒有被回收。繼續查看此對象的引用鏈:

原來是Control.cachedLayoutEventArgs在作怪!

現在問題比較清楚了:雖然我們已經銷毀了holderButton,並不再引用它,但是.NET的內部代碼仍然在引用它,而holderButton.Tag所引用的對象自然也不能被回收了。

針對我們的問題,只需要在36行的位置加上holderButton.Tag = null就可以了。而更通用的情況,則應該在Disposed事件中(或重寫相應的方法)將對數據的引用設置為null。

在網上搜索cachedLayoutEventArgs,發現也有人遇到相關的問題,可以參考http://book.3you.cc/bc/Print.asp?ArticleID=297177的內容。

出處:http://www.cnblogs.com/brucebi/archive/2013/04/03/2997490.html

  1. 上一頁:
  2. 下一頁:
Copyright © 程式師世界 All Rights Reserved