.NET中常见的内存泄漏和解决办法

王朝学院·作者佚名  2016-08-27
窄屏简体版  字體: |||超大  

在.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方法,如果忘记调用,就造成"内存泄漏"。

 
 
 
免责声明:本文为网络用户发布,其观点仅代表作者个人观点,与本站无关,本站仅提供信息存储服务。文中陈述内容未经本站证实,其真实性、完整性、及时性本站不作任何保证或承诺,请读者仅作参考,并请自行核实相关内容。
 
 
© 2005- 王朝網路 版權所有 導航