📣读完这篇文章里你能收获到
- 托管内存跟非托管内存泄露的常见场景总结
- 了解到常见的非托管资源有哪些
- 非托管资源使用的最佳实践
文章目录
一、托管内存泄露
1. 静态变量
静态变量和它们引用的任何东西都不会被垃圾收集
public class MyClass
{
static List<MyClass> _instances = new List<MyClass>();
public MyClass()
{
_instances.Add(this);
}
}
2. 本地缓存
本地缓存使用时,如果不加以限制,会导致内存泄露并抛出OutOfMemoryException异常,使用时需注意:
- 删除一段时间未使用的缓存
- 限制缓存大小
3. 订阅事件
一旦你订阅了一个事件,这个对象就拥有了对你的类的引用
public class MyClass
{
public MyClass(WiFiManager wiFiManager)
{
wiFiManager.WiFiSignalChanged += OnWiFiChanged;
}
private void OnWiFiChanged(object sender, WifiEventArgs e)
{
// do something
}
}
4. 在匿名方法中捕获成员
public class MyClass
{
private JobQueue _jobQueue;
private int _id;
public MyClass(JobQueue jobQueue)
{
_jobQueue = jobQueue;
}
public void Foo()
{
_jobQueue.EnqueueJob(() =>
{
Logger.Log($"Executing job with ID {_id}");
// do stuff
});
}
}
在这段代码中,成员_id是捕获在匿名方法中,因此实例也被引用。这意味着虽然JobQueue存在并引用该作业委托,它还将引用MyClass.
解决方案非常简单——分配一个局部变量:
public class MyClass
{
public MyClass(JobQueue jobQueue)
{
_jobQueue = jobQueue;
}
private JobQueue _jobQueue;
private int _id;
public void Foo()
{
var localId = _id;
_jobQueue.EnqueueJob(() =>
{
Logger.Log($"Executing job with ID {localId}");
// do stuff
});
}
}
二、非托管内存泄露
1. 常见的非托管资源
常见的非托管资源有:文件句柄、数据流、数据库连接、网络连接
2. 使用Dispose模式
定义封装非托管资源的对象时,建议在公共 Dispose 方法中提供必要的代码以清理非托管资源。 通过提供 Dispose 方法,对象的用户可以在使用完对象后显式释放资源。 使用封装非托管资源的对象时,务必要在需要时调用 Dispose。在使用时加上using语句
3. 最佳实践
3.1 读取文件流
public void Foo()
{
//使用
using (var stream = new FileStream(@"C:\Temp\SomeFile.txt",FileMode.OpenOrCreate))
{
// do stuff
}
// stream.Dispose() 即使发生异常也会被调用
}
3.2 自行分配非托管资源
添加析构函数,对象回收时如果没调用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)
{
// 释放托管资源
}
// 释放非托管资源
Marshal.FreeHGlobal(_bufferPtr);
_disposed = true;
}
public void Dispose()
{
Dispose(true);
//确保在垃圾回收时不会调用析构函数
GC.SuppressFinalize(this);
}
~MyClass()
{
Dispose(false);
}
}