2

Is there any way to get GC.Collect() to be called before throwing an OutOfMemoryException?

I suppose I'm looking for a way for it to do the following code flow:

Try to Allocate Memory
On Pass Return
Call GC.Collect()
Try to Allocate Memory
On Fail Throw New OutOfMemoryException()

I'm writing a caching implementation and currently I'm running into memory exceptions so currently to resolve it I am using:

If GC.GetTotalMemory(False) >= cache.CacheMemoryLimit + (100 * 1024 * 1024) Then
    // When Total Memory exceeds CacheMemoryLimit + 100MB
    GC.Collect()
End If
Seph
  • 8,472
  • 10
  • 63
  • 94
  • 4
    I think that the runtime already does a garbage collection run if you're low on memory when creating new objects. – sisve Jan 14 '11 at 06:02
  • 1
    @Simon true, usually the OutOfMemoryException is thrown is case the garbage collector cannot reclaim anymore memory for your instance, which mean static references or other kind of memory leak. The Class Loader is particularly quite leaky because of static references. – dvhh Jan 14 '11 at 06:15

4 Answers4

5

Maybe I'm not understanding your question, but wouldn't it be possible for you to just catch OutOfMemoryException's thrown and call GC.Collect there? Toss the try/catch inside a loop that continues until you've finished your task and make sure it has the ability to clean itself up.

bool isFinished = false;
while (isFinished) {
  try {
    // do operations in here
  } catch (OutOfMemoryException oom) {
    GC.Collect();
  }

  // if you're done...
  isFinished = true;
}

Pardon the use of C# pseudo instead of VB, I try not to work in VB unless I have no choice.

AndyM84
  • 139
  • 4
  • Thanks, this helped find my problem, objects in memory had not yet finished finalizing before the application was trying to allocate further memory. – Seph Jan 14 '11 at 06:18
  • Be careful, my testing seemed to show that in some cases the .NET framework throws other exceptions when you are really out of memory (I've seen `ArgumentException` when allocating arrays for example), and sometimes you will get (uncatchable) Win32 errors that will just kill your program instantly. – jrh Jan 18 '17 at 15:20
5

Actually .NET will happily throw out-of-memory exceptions when instead it could have garbage-collected and succeeded. This is one of my few gripes with Microsoft's .net implementation. Unit tests that allocate and immediately discard a lot of memory in one or more threads appear to pass. I think the problems start, however, once large chunks of older generations go out of scope. That stuff isn't picked up right away.

So for example if you have code that reads a million XmlDocuments from disk one at a time, chances are reasonable that you'll get an OutOfMemory exception unless you GC.Collect() every few documents.

EDIT: http://social.msdn.microsoft.com/forums/en-US/clr/thread/52a7eb17-ac05-470c-b063-a78427cd4406/

Jugglist
  • 189
  • 2
  • 4
4

The whole point of the Out of Memory exception is to let you know that you're out of memory, after all efforts to GC the system have failed. If you're truly working on a caching system, then you should consider looking at Weak or Soft References. These allow the system to discard items when memory gets tight, even though they have references to them.

In a caching system, when you look up an object, one second you'll get the result, the next second you'll get a NULL because memory got tight enough to force the system to discard the weak referenced objects. At this point your cache recreates the object, and puts it back in the cache, and you continue on your way as if it was the first time you referenced the object anyway.

Will Hartung
  • 115,893
  • 19
  • 128
  • 203
3

Calling GC.Collect can fix timing related OutOfMemory exceptions. As Jugglist mentioned above ".NET will happily throw out-of-memory exceptions when instead it could have garbage-collected and succeeded." I have a high speed soft real time video processing system that would very occasionally throw OutOfMemory exceptions when under heavy load. A strategic placement of GC.Collect calls in a known processing deadband (before the next camera trigger) fixed these problem. Granted another option would have been to manually dispose of the objects as needed but then why the heck are we using a managed environment. This problem wass likely exacerbated by the fact that my app was compiled x86 memory model. So if you are doing something exceptionally memory intensive and feel you have a place in the code where you think it is appropriate to do garbage collection then do it. This has been widely discussed here: What's so wrong about using GC.Collect()?

Community
  • 1
  • 1
Eric H
  • 31
  • 1