12

Are there any memory leaks when I throw an exception from a constructor like the following?

class Victim
{
    public string var1 = "asldslkjdlsakjdlksajdlksadlksajdlj";

    public Victim()
    {
        //throw new Exception("oops!");
    }
}

Will the failing objects be collected by the garbage collector?

Peter Mortensen
  • 30,738
  • 21
  • 105
  • 131
Mik Kardash
  • 630
  • 8
  • 17
  • Barely related, but useful tip: Be careful about exceptions thrown in constructors of Controls. It can break the designer for the control/form. I got round it by having an Initialise() method and calling that externally (but I don't like it). – Neil Barnwell May 29 '09 at 14:50

5 Answers5

27

In general this is safe from the perspective of not leaking memory. But throwing exceptions from a constructor is dangerous if you allocate unmanaged resources in the type. Take the following example

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);
  }
}

This class will leak memory every time you try to create even if you use a using block. For instance, this leaks memory.

using ( var f = new Foo() ) {
  // Won't execute and Foo.Dispose is not called
} 
JaredPar
  • 733,204
  • 149
  • 1,241
  • 1,454
  • Need to make it clear that not only does the body of the using statement not execute...but the the .Dispose() method of Foo does not get called, because no instance was created to call .Dispose() on. – jrista May 29 '09 at 14:55
  • I'm seriously late to the party here, but wouldn't a try-finally in the constructor fix this without issue? – Gusdor Oct 11 '17 at 11:46
  • @Gusdor: What would *eventually* fix this would be to have another disposable class wrapping the unmanaged resource, which does absolutely nothing in its constructor except allocate the resource. Then even when `Foo` throws in the constructor, the resource wrapper object can still be collected and appropriately free the resource (albeit not immediately). – Chris Charabaruk Dec 06 '22 at 18:47
12

Throwing exceptions from a constructor should be fine if you have created no unmanaged resources. However, if you do create unmanaged resources in the constructor, the whole body of that constructor, including throws, should be wrapped in a try/catch. To steal JaredPar's great example:

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);
  }
}

The following would now work:

using ( var f = new Foo() ) {
  // Won't execute, but Foo still cleans itself up
}
jrista
  • 32,447
  • 15
  • 90
  • 130
4

Funny, because I helped with a similar question just yesterday.

It's a bigger issue if you have a derived type, because some parts of the derived type will initialize but not others. From a memory perspective it doesn't really matter, because the garbage collector knows what's where. But if you have any unmanaged resources (implement IDisposable) things can get murky.

Community
  • 1
  • 1
Joel Coehoorn
  • 399,467
  • 113
  • 570
  • 794
2

Yes, the garbage collector will reclaim the managed resources already allocated in the object. If you've initialised any unmanaged resources, you will need to clean those up yourself in the normal way.

Rob
  • 47,999
  • 5
  • 74
  • 91
1

It depends on what other resources you've aquired before the exception is thorwn. I don't think throwing exceptions in a constructor is great, but throwing them in finalizers or dispose() is much much worse.

n8wrl
  • 19,439
  • 4
  • 63
  • 103
  • 2
    "throwing them in finalizers or dispose() is much much worse.". It can be acceptable to throw in Dispose - for example FileStream will throw if it is unable to flush the Stream (e.g. due to a network error). Avoid throwing if you can, but do so if you must. In the case of FileStream, swallowing the exception would be much more dangerous than throwing, as it would give the caller the impression that the file had been written successfully. – Joe May 29 '09 at 17:22