当我从如下构造函数中抛出异常时是否有任何内存泄漏?
class Victim
{
public string var1 = "asldslkjdlsakjdlksajdlksadlksajdlj";
public Victim()
{
//throw new Exception("oops!");
}
}
垃圾收集器是否会收集失败的对象?
答案 0 :(得分:25)
一般来说,从不泄漏内存的角度来看这是安全的。但是,如果在类型中分配非托管资源,则从构造函数中抛出异常会很危险。采取以下示例
public class Foo : IDisposable {
private IntPtr m_ptr;
public Foo() {
m_ptr = Marshal.AllocHGlobal(42);
throw new Exception();
}
// Most of Idisposable implementation ommitted for brevity
public void Dispose() {
Marshal.FreeHGlobal(m_ptr);
}
}
即使您使用using块,每次尝试创建时,此类都会泄漏内存。例如,这会泄漏内存。
using ( var f = new Foo() ) {
// Won't execute and Foo.Dispose is not called
}
答案 1 :(得分:10)
如果您没有创建非托管资源,那么从构造函数中抛出异常应该没问题。但是,如果您确实在构造函数中创建了非托管资源,那么构造函数的整个主体(包括throws)应该包装在try / catch中。窃取JaredPar的好例子:
public class Foo : IDisposable {
private IntPtr m_ptr;
public Foo() {
try
{
m_ptr = Marshal.AllocHGlobal(42);
throw new Exception();
}
catch
{
Dispose();
throw;
}
}
// Most of Idisposable implementation ommitted for brevity
public void Dispose() {
Marshal.FreeHGlobal(m_ptr);
}
}
现在可以使用以下内容:
using ( var f = new Foo() ) {
// Won't execute, but Foo still cleans itself up
}
答案 2 :(得分:4)
很有趣,因为昨天我帮助了similar question。
如果您有派生类型,这是一个更大的问题,因为派生类型的某些部分将初始化而不是其他部分。从内存的角度来看,它并不重要,因为垃圾收集器知道它在哪里。但是如果你有任何非托管资源(实现IDisposable),事情就会变得模糊不清。
答案 3 :(得分:2)
是的,垃圾收集器将回收已在对象中分配的托管资源。如果您已初始化任何非托管资源,则需要以正常方式自行清理这些资源。
答案 4 :(得分:1)
这取决于您在异常被授予之前获得的其他资源。我不认为在构造函数中抛出异常是很好的,但是将它们放在终结器或dispose()中会更糟糕。