不是GC打酱油,是人打酱油
2011-09-22 22:00 乱世文章 阅读(258) 评论(0) 编辑 收藏 举报不是GC打酱油,是人打酱油
之前在一个技术群里看到有人讨论内存释放问题,引发对GC的讨论,大体结论是GC不可靠,GC是打酱油的。
之所以得出这个结论,是因为我们大多时候使用的是GC.Collect(),同时并未提供大内存对象的析构函数并在析构函数中释放内存。
如有类:
public class BufWrapper
{
private byte[] buf;
public BufWrapper()
{
buf=new byte[1024*1024*2];
}
~BufWrapper()
{
buf=null;
}
}
对此写测试代码:
BufWrapper br=null;
while(true)
{
br=null;
br=new BufWrapper();
}
这样的代码必定导致内存问题,原因在于虽然每次循环将上次创建的br设置为null使其引用数得以置零,但 .net的垃圾回收机制在默认情况下并不立即执行其析构函数,从而此对象中的buf不能被立即释放。所以,考虑为测试代码增加GC.Collect():
BufWrapper br=null;
while(true)
{
br=null;
br=new BufWrapper();
GC.Collect();
}
测试会发现,内存有所控制,但长时间观察,仍然呈波动上升趋势,而这应该正是大家怀疑GC的原因所在。其实,GC.Collect()也并不保证垃圾对象被立即回收。所以,一般建议为对象实现IDisposable接口,通过代码主动释放:
public class BufWrapper:IDisposable
{
private byte[] buf;
public BufWrapper()
{
buf=new byte[1024*1024*2];
}
~BufWrapper()
{
this.Dispose();
}
public virtual void Dispose()
{
buf=null;
}
}
测试代码:
BufWrapper br=null;
while(true)
{
if(br!=null)br.Dispose();
br=new BufWrapper();
}
测试发现,以上代码不存在内存问题。
但是,这里就存在一个问题,如果现有对象并未实现IDisposable接口,或实现的接口方法中并未释放资源怎么办?GC.Collect()不能保证立即释放资源,怎么办?
其实GC还有一个方法:GC.WaitForPendingFinalizers()。使用以下代码(甚至BufWrapper不提供析构函数):
public class BufWrapper
{
private byte[] buf;
public BufWrapper()
{
buf=new byte[1024*1024*2];
}
}
测试代码:
BufWrapper br=null;
while(true)
{
br=null;
br=new BufWrapper();
GC.Collect();
GC.WaitForPendingFinalizers();
}
曾经怀疑GC的人,会惊奇的发现,以上代码也不存在内存问题。
这个方案在不改动现有类(未实现释放资源的IDisposable接口)的情况下,解决了内存问题,但它也存在一些的问题,如果有机会,在以后再和大家详细讨论。
注:对于技术问题,我喜欢提供详细方案,但不愿意对细节做太多解释,只希望有需要的人能以此为基础自行比较研究,从而获取更多