I am a little bit confused about the fact that we can just catch an OutOfMemoryException
using a try/catch block.
Given the following code:
Your OutOfMemory()
method creates data structure (the List<byte[]>
) that is local to the method's scope. While your thread of execution is inside the OutOfMemory
method, the current stack frame is considered the GC root for the List. Once your thread ends up in the catch block, the stack frame has been popped and the list has effectively become unreachable. Therefore the garbage collector determines that it can safely collect the list (which it does as you have observed in your memory graph).
Not sure whether this answers your question, but a (simplified) explanation on how it decides what objects to clean up is this:
The garbage collector takes every running thread in your program and marks all top-level objects, which means all objects accessible from the stack frames (that is, all the objects pointed by local variables at the points where execution currently is) as well as all objects pointed by static fields.
Then, it marks the next level objects, which means all the objects pointed by all the fields of the previously marked objects. This step is repeated until no new objects are marked.
Because C# doesn't allow pointers in the normal context, once the previous step is completed, it's guaranteed that the non-marked objects are not accessible by subsequent code and therefore can be cleaned up safely.
In your case, if the objects you have allocated to add pressure to the memory manager are not kept by reference, it means that the GC will have the chance to clean them up. Also, keep in mind that OutOfMemoryException refers to the managed memory of your CLR program, while the GC works a little outside of that "box".
The OutOfMemoryException is quite possibly thrown because you are running a 32 bit program, with the memory graph you have not indicated how much ram the system has, so perhaps try building it as a 64 bit, and maybe use MemoryFailPoint to prevent this occurring anyway.
You could also let us know what is in the OutOfMemory() function for a clearer picture.
P.S. StackOverFlow is the only error which cannot be handled.
Edit: as mentioned above, and I thought it only logical and hence didn't mention it earlier, if you for example try to allocate more memory than you have 'spare' then it is not possible to do so and an exception occurs. As you are allocating large arrays with your data.Add() it falls over before the final 'illegal' add occurs, hence there is still free memory.
So I would assume that it is at this point data.Add(buffer); the issue occurs during the building of the array when you trip the 2GB process limit by adding a 400MB byte array to 'data', e.g. an array of around 1 billion objects at 4 bytes a piece I would expect to be around 400MB.
P.S. Up until .net 4.5 max process memory allocation is 2GB, after 4.5 larger are available.
The GC makes an analysis on the references that are used in the program, and can throw away any object that isn't used anywhere.
An OutOfMemoryException
doesn't mean that the memory is completely depleted, it just means that a memory allocation failed. If you tried to allocate a large memory area at once, there may still be plenty of free memory left.
When there isn't enough free memory for an allocation, the system does a garbage collection to try to free up memory. If there still isn't enough memory for the allocation, it will throw the exception.
A StackOverflowException
is not possible to handle, because it means that the stack is full, and it's not possible to remove anything from it as it is with the heap. You would need more stack space to continue running the code that would handle the exception, but there is no more.
The reason you can catch an OutOfMemoryException is because the language designer decided to let you. The reason this is sometimes (but not usually) practical is because it's a recoverable situation in some cases.
If you attempt to allocate a huge array, you may get an OutOfMemoryException, but the memory for that huge array will not actually have been allocated - so other code will still be able to run without problems. Also, the stack unwinding due to the exception may cause other objects to be come eligible for garbage collection, further increasing the amount of memory available.