.NET中常見的內存泄漏和解決辦法


 

在.NET中,雖然CLR的GC垃圾回收器幫我們自動回收托管堆對象,釋放內存,最大程度避免了"內存泄漏"(應用程序所占用的內存沒有得到及時釋放),但.NET應用程序"內存泄漏"的問題還是會存在,如果不加以注意,"內存泄漏"時有發生。


有關流以及Reader或Writer引起的內存泄漏

 

比如,把文件讀取到流中:

 

public static string ReadFile()
{
    var filePath = @"硬盤地址";
    var sr = new StreamReader(filePath);
    return sr.ReadToEnd();
}

 

以上,StreamReader在讀取數據后沒有解釋銷毀,存在"內存泄漏"。正確的做法是在使用完后及時關閉。

 

public static string ReadFile()
{
    var filePath = @"硬盤地址";
    using(var sr = new StreamReader(filePath))
    {
        return sr.ReadToEnd();
    }
}

 

或者

 

public static string ReadFile()
{
    var filePath = @"硬盤地址";
    var sr = new StreamReader(filePath);
    var result = sr.ReadToEnd();
    sr.Close();
    return result;
}

 

以上,需要我們注意的是:當通過某種流的構造函數創建的對象實例,注意及時關閉。

 

有時候,通過某個方法返回某種流的對象實例,也會忘記關閉。比如以下:

 

//創建字節數組
var data = new byte[1024];
var client = new TcpClient();
//從TCP實例方法返回流
var stream = client.GetStream();
//把流讀到字節數組中
int bytesLength = stream.Read(data, 0, data.Length);
//字節數組轉換城字符串
var result = System.Text.Encoding.ASCII.GetString(data, 0, bytesLength);

 

正確的寫法應該是:

 

//創建字節數組
var data = new byte[1024];
var client = new TcpClient();
//從TCP實例方法返回流
var stream = client.GetStream();
//把流讀到字節數組中
int bytesLength = stream.Read(data, 0, data.Length);
stream.Close();
//字節數組轉換城字符串
var result = System.Text.Encoding.ASCII.GetString(data, 0, bytesLength);

 

同理,其它與流有關的類,我們也需要注意在用完后及時關閉:

 

● FileStream
● MemoryStream
● StreamReader
● TextWriter
......

 

靜態引用引起的內存泄漏

 

對於靜態實例來說,除非應用程序關閉,對應的內存一直得不到釋放。比如有如下遵循"Siingleton"模式的類(沒考慮線程安全)。

 

public class MySingletonClass
{
    private static MySingletonClass myInstance;
    private static List<IAmBig> bigObjects = new List<IAmBig>();
    private MySingletonClass(){}
    public static MySingletonClass MyInstance
    {
        get
        {
            if(myInstance == null)
            {
                myInstance = new MySingletonClass();
            }
            return myInstance;
        }
    }
    public static IAmBig CreateBigObject()
    {
        var bigObject = new IAmBig();
        bigobject.AllocateMemory(4096);
        bigObjects.add(bigObject);
        return bigObject;
    }
}
public class IAmBig
{
    
}

 

以上,每次調用CreateBigObject靜態方法,都往List<IAmBig>類型集合中添加,由於MySingletonClass靜態類實例一直存在於應用程序的生命周期,再加上GC不會釋放bigObjects這個集合對象實例,於是就出現了"內存泄漏"。解決辦法是避免讓靜態實例引用其它實例對象,避免出現靜態實例的鏈式引用。

 

委托引起的內存泄漏

 

比如有2個委托形成的委托鏈。

 

var objectOne = new ObjectOne();
var objectTwo = new ObjectTwo();
objectOne.StateChanged += objectTwo.StateChangedEventHandler;
objectTwo.Dispose();

 

以上,把objectTwo的委托注冊給了objectOne,這樣objectOne和objectTwo有依賴關系,形成了依賴鏈。只有當objectOne被釋放,才能釋放objectTwo。如果objectOne恰巧是全局靜態實例,那在應用程序的生命周期內,objectTwo一直得不到內存釋放,造成了"內存泄漏"。

 

解決辦法是在調用objectTwo的Dispose方法之前,先解開兩者的依賴關系。修改如下:

 

var objectOne = new ObjectOne();
var objectTwo = new ObjectTwo();
objectOne.StateChanged += objectTwo.StateChangedEventHandler;
......
objectOne.StateChanged -= objectTwo.StateChangedEventHandler;
objectTwo.Dispose();

 

非托管資源引起的內存泄漏

 

public class MyUnManagedExample
{
    public void Allocate()
    {
        IntPtr pointer = Marshal.AllocHGlobal(1024);
    }
}

 

對於創建的非托管類型的實例ponter,需要顯式釋放。

 

Marshal.FreeGlobal(pointer);

 

實現IDisposable接口的類引起的內存泄漏

 

所有實現IDisposable接口的類都有一個Dispose方法,如果忘記調用,就造成"內存泄漏"。


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM