title: GC.AddMemoryPressure() not enough to trigger the Finalizer queue execution on time
body: <p>We have written a custom indexing engine for a multimedia-matching project written in <code>C#</code>.  </p>
  
  <p>The indexing engine is written in unmanaged <code>C++</code> and can hold a significant amount of unmanaged memory in the form of <code>std::</code> collections and containers.  </p>
  
  <p>Every unmanaged index instance is wrapped by a managed object; the lifetime of the unamanaged index is controlled by the lifetime of the managed wrapper.</p>
  
  <p>We have ensured (via custom, tracking C++ allocators) that every byte that is being consumed internally by the indexes is being accounted for,  and we update (10 times per second) the managed garbage collector's memory pressure value with the deltas of this value  (Positive deltas call <code>GC.AddMemoryPressure()</code>, negative deltas call <code>GC.RemoveMemoryPressure()</code>).</p>
  
  <p>These indexes are thread-safe, and <strong>can be shared by a number of C# workers, so there may be multiple references in use for the same index.  For that reason, we can not call <code>Dispose()</code> freely, and instead rely on the garbage collector to track reference sharing and eventually to trigger the finalization of the indexes once they are not in use by a worker process.</strong></p>
  
  <p>Now, the problem is that <strong>we are running out of memory</strong>. Full collections are in fact executed relatively often, however, with the help of a memory profiler, we can find a very large number of "dead" index instances being held in the finalization queue at the point where the process runs out of memory after exhausting the pagination file.      </p>
  
  <p>We can actually circumvent the problem if we add a watchdog thread that calls  <code>GC::WaitForPendingFinalizers()</code> followed by a <code>GC::Collect()</code> on low memory conditions, however, from what we have read, calling <code>GC::Collect()</code> manually severely disrupts garbage collection efficiency, and we don't want that.</p>
  
  <p>We have even added, to no avail, a pessimistic pressure factor  (tried up to 4x)  to exaggerate the amount of unmanaged memory reported to the .net side, to see if we could coax the garbage collector to empty the queue faster. It seems as if the thread that processes the queue is completely unaware of the memory pressure.</p>
  
  <p>At this point we feel we need to implement a manual reference counting to <code>Dispose()</code> as soon as the count reaches zero, but this seems to be an overkill, especially because the whole purpose of the memory pressure API is precisely to account for cases like ours.</p>
  
  <p>Some facts:</p>
  
  <ul>
  <li>.Net version is 4.5 </li>
  <li>App is in 64-bit mode </li>
  <li>Garbage collector is running in concurrent server mode. </li>
  <li>Size of an index is ~800MB of unmanaged memory  </li>
  <li>There can be up to 12 "alive" indexes at any point in time.  </li>
  <li>Server has 64GB of RAM</li>
  </ul>
  
  <p><strong>Any ideas or suggestions are welcome</strong></p>