Steve Sheldon
Steve Sheldon

Reputation: 6651

How to determine appropriate argument to GC.AddMemoryPressure()?

I am writing an application in C# that makes use of a 3rd party COM DLL, this dll creates a lot of resources (like bitmaps, video, data structures) in unmanaged memory. While digging around I came across the following call for the Garbage Collector:

GC.AddMemoryPressure(long long bytesAllocated)

It is documented in MSDN here:

http://msdn.microsoft.com/en-us/library/system.gc.addmemorypressure.aspx

This sounds like something I should be calling since this external dll is createing a lot of resources the CLR is unaware of.

I guess I have two questions...

  1. How do I know how much memory pressure to add when the dll is 3rd party and it's not possible for me to know exactly how much memory this dll is allocating.
  2. How important is it to do this?

Upvotes: 7

Views: 4015

Answers (4)

plinth
plinth

Reputation: 49199

Let's say I have an object like this:

public class SomeImageType : IDisposable
{
    public int Width { get; private set; }
    public int Height { get; private set; }
    public PixelFormat PixelFormat { get; private set; }
    IntPtr ImageData { get; private set; }
    // implementation of constructor and IDisposable not shown
}

How much memory does this take? 20 bytes + object overhead? When it comes time for collection, if this object has no references it makes no difference at all. But does it take that 20 bytes? Hell no - it's an image. So take that 20 and multiply it by the width, the height and the number of bytes per pixel and you have something that takes up (possibly) megabytes. By telling the GC to add memory pressure, you are saying that there is an iceberg in there chewing up resources. When you dispose the object, you will, of course, free that memory and tell the GC to release that pressure. These two calls let you hint to the GC that there are strange things afoot at the Circle K, and maybe it would like to schedule collection differently.

Upvotes: 3

Daniel Earwicker
Daniel Earwicker

Reputation: 116714

In any mixed native/managed process, there is a mixture of native/managed memory usage. If there is no GC-controlled relationship between the two, then there would be no need for this API. For example, if there are certain deterministic state changes in the managed code that cause native memory to be allocated and deallocated, then nothing the GC can do will ever force native memory to be released.

However, very often there is native memory held by managed objects that have finalizers. So the GC can reduce the size of the native heap, simply by triggering a collection and getting those finalizers to run.

Therefore if you have a lot of that going on, it could well be necessary to call this API (just like the documentation says).

As for how much you should tell it, that's probably not something you can cook up an answer for by pure analysis, especially with a 3rd party library. You need to run Performance Monitor, run a test that allocates a lot of the 3rd party objects, and look at the Native Bytes and CLR memory counters to see how they relate.

As you're using a COM object, you could in fact deterministically force instances to clean up when you know you no longer need them, by using Marshal.ReleaseComObject. Note that you need to use a goofy loop to make it get rid of the object:

while (Marshal.ReleaseComObject(obj) != 0) 
{
}

Upvotes: 5

Cylon Cat
Cylon Cat

Reputation: 7201

The most important thing you can do is call the Dispose() method on these third-party DLL classes, and be absolutely fanatical about doing it. (Or, of course, using "using" blocks, since they call Dispose() automatically on exit from the block.)

The thing about unmanaged resources is that .NET classes that wrap or use them need to hava finalizer, as well as implementing IDisposable() pattern. If Dispose() is called, it should take care of the unmanaged resources immediately, and it should also suppress the finalizer. If you don't call Dispose(), then you have real memory issues, and adding "pressure" to the garbage collector won't fix it.

The underlying logic of the garbage collector is that any class that has an active finalizer isn't dealt with until all other possibilities are already exhausted. That means gen0, gen1, and gen2 cleanup have already occurred. A finalizer that hasn't been suppressed is almost as bad as a memory leak.

So call Dispose(). Make sure you never miss it.

EDIT: Just noticed that the 3rd party DLL is a COM DLL. Best practice is to make sure that your .NET wrappers implement IDisposable() fully (not just providing a Dispose method). Then make sure that you're always calling Dispose() when you're done with the COM objects.

Upvotes: 2

SLaks
SLaks

Reputation: 887797

It is not important for you to do this, unless you're noticing memory issues that you believe are caused by the GC.

Upvotes: 1

Related Questions