0

I created this simple C# program expecting it to throw OutOfMemoryException. Problem is it works perfectly without using all my laptop memory. Why? How can I make it leak all the memory?

static void Main(string[] args)
{
    for (int i = 0; true; i++)
    {
        new MustDispose();
    }
}


class MustDispose
{
    public MustDispose()
    {
        StreamReader r = null;
        r = File.OpenText("C:\\Users\\rverdelli\\Desktop\\asd.txt");
        if (r.EndOfStream) return;
        Console.WriteLine(r.ReadToEnd());
    }

}
rverdelli
  • 48
  • 5

2 Answers2

3

Each iteration of your loop isn't storing any state beyond the scope of that iteration, so when it's over all of the memory allocated in that iteration can be freed.

If you want to run out of memory you'll need to hold onto the memory that you're allocating.

Here's a simple enough example that shouldn't have a problem running you out of memory:

var data = Enumerable.Range(0, int.MaxValue)
    .Select(i => new int[int.MaxValue])
    .ToArray();
Console.WriteLine(data.Length);
Servy
  • 202,030
  • 26
  • 332
  • 449
3

In your method MustDispose(), there is one character which is important:

}

This defines the end of a scope, which means that local variables are no longer valid. Being no longer valid tells the garbage collector to remove the local variables, particularly r as well as all temporary results which haven't even been assigned to a variable (i.e. the boolean return value of r.EndOfStream and the string r.ReadToEnd()).

There are various possibility to eat all memory, e.g.

var keeper = new List<byte[]>();
while(true)
{
    keeper.Add(new byte[1024*1024]);
}

If the IDisposable interface is implemented correctly, the Finalizer will check if Dispose() has already been called. If not, the Finalizer will do so.

Community
  • 1
  • 1
Thomas Weller
  • 55,411
  • 20
  • 125
  • 222
  • You might also mention that, although calling `Dispose` is *recommended*, the `StreamReader` finalizer will call it automatically when the object is collected. So in this case the drawback to not calling `Dispose` explicitly is that cleanup happens a little later. – Jim Mischel Sep 26 '14 at 16:12
  • 1
    `r` isn't going to be that big. The string returned by `r.ReadToEnd()` has the potential to be *far* larger than that reader, and it's scope is actually smaller than the scope of that method; it's lifetime is the scope of the invocation of `WriteLine`. – Servy Sep 26 '14 at 16:13
  • The scope of the variable is not the same as the lifetime of the object. The CLR can detect when objects are no longer referenced, so they typically becomes eligible for GC much sooner than the scope would allow. – Brian Rasmussen Sep 26 '14 at 16:53
  • @BrianRasmussen: you're right. Question is, whether that additional information would help the OP or confuse him. So, sometimes the quality of an answer is defined by omitting unnecessary details. And perhaps the comments are exactly the right place to mention such things. Thank you for doing it. – Thomas Weller Sep 26 '14 at 17:06