3

EDIT: I reformulated it to be a question and moved the answer to the answers part...

In a relatively complex multithreaded .NET application I experienced OutOfMemoryException even in the cases I could think there is no reason for it.

The situation:

  • The application is 32bit.
  • The application creates lot of (thousands) short lived objects that are considered small (less than approx. 85kB).
  • Additionaly it creates some (hundreds) short lived objects that are considered large (greater than approx. 85kb). This implies these objects are allocated at LOH (large object heap).
  • Both classes for these objects define finalizer (~MyFinalizer(){...}).

The symptoms:

  • OutOfMemoryException
  • Looking at the app via memory profiler, there are thousands of the small objects eligible for collection, but not collected and thus block large amount of memory.

The questions:

  • Why the app exhausts entire heap?
  • Why there is lot of "dead" objects still present in the memory?
sharpener
  • 1,383
  • 11
  • 22
  • Interesting post, but if you want a question or advice you may precise it. Otherwise, thanks for the tips ;) – AFract Jul 31 '14 at 18:14
  • 1
    Your classes probably should not have a user-defined finalizer. I have seen many finalizers written in user code, but only one that truly needed to be there. It's [this one](https://github.com/libgit2/libgit2sharp/blob/vNext/LibGit2Sharp/Core/NativeMethods.cs#L37-L40) and it took all the work in [#249](https://github.com/libgit2/libgit2sharp/pull/249) and [#358](https://github.com/libgit2/libgit2sharp/pull/358) to agree to add it. – Sam Harwell Aug 01 '14 at 03:45
  • 1
    The LOH is not called the Gen2 Heap. Add a source if you think I'm wrong. – H H Aug 04 '14 at 07:06
  • You are right. I confused two things, I removed this. I wanted to help others but still was confused a bit. Feel free to improve the text. I think it can be useful for someone, because it might take lot of time to sort the problem out. – sharpener Aug 05 '14 at 15:09
  • For the ones who vote down: Feel free to tell me, what is wrong, I could learn something and don't make mistakes next time. Thanks... – sharpener Aug 05 '14 at 15:17

2 Answers2

2

After some deep investigation I have found the reason. As it took some time, I would like to make it easy for others suffering the same problem.

The reasons:

  • App has only approx 2GB of virtual address space.
  • The LOH is by design not compacted and thus might get fragmented very quickly, but for the mentioned count of large objects it should not be any problem.
  • Due to the design of the Garbage Collector, if there is an object defining the finalizer (even just empty), it is considered as Gen2 object and is placed into GC's finalization queue. This implies, until it is finalized (the MyFinalizer called) it just blocks the memory. In the case of mentioned app the GC thread running the finalizers didn't get the chance to do its work as quickly as needed and thus the heap was exhausted.

The Solution:

Do not use the finalizer for such "dynamic" objects (high volume, short life), workaround the finalization code in other way...

Very useful sources:

Community
  • 1
  • 1
sharpener
  • 1,383
  • 11
  • 22
0

Try using a profiler, such as:

  • ANTS Memory Profiler
  • ANTS Performance Profiler
  • Jetbrains Performance Profiler

For LOH force a GC with:

            GC.Collect();
            GC.WaitForPendingFinalizers();
Fra
  • 91
  • 5