What is the point of using GC.AddMemoryPressure with an unmanaged resource?
Asked Answered
V

4

19

I've read about this issue on MSDN and on CLR via c#.

Imagine we have a 2Mb unmanaged HBITMAP allocated and a 8 bytes managed bitmap pointing to it. What's the point of telling the GC about it with AddMemoryPressure if it is never going to be able to make anything about the object, as it is allocated as unmanaged resource, thus, not susceptible to garbage collections?

Vainglory answered 19/7, 2009 at 3:36 Comment(0)
P
10

The point of AddMemoryPressure is to tell the garbage collector that there's a large amount of memory allocated with that object. If it's unmanaged, the garbage collector doesn't know about it; only the managed portion. Since the managed portion is relatively small, the GC may let it pass for garbage collection several times, essentially wasting memory that might need to be freed.

Yes, you still have to manually allocate and deallocate the unmanaged memory. You can't get away from that. You just use AddMemoryPressure to ensure that the GC knows it's there.

Edit:

Well, in case one, I could do it, but it'd make no big difference, as the GC wouldn't be able to do a thing about my type, if I understand this correctly: 1) I'd declare my variable, 8 managed bytes, 2mb unmanaged bytes. I'd then use it, call dispose, so unmanaged memory is freed. Right now it will only ocuppy 8 bytes. Now, to my eyes, having called in the beggining AddMemoryPressure and RemoveMemoryPressure at the end wouldn't have made anything different. What am I getting wrong? Sorry for being so anoying about this. -- Jorge Branco

I think I see your issue.

Yes, if you can guarantee that you always call Dispose, then yes, you don't need to bother with AddMemoryPressure and RemoveMemoryPressure. There is no equivalence, since the reference still exists and the type would never be collected.

That said, you still want to use AddMemoryPressure and RemoveMemoryPressure, for completeness sake. What if, for example, the user of your class forgot to call Dispose? In that case, assuming you implemented the Disposal pattern properly, you'll end up reclaiming your unmanaged bytes at finalization, i.e. when the managed object is collected. In that case, you want the memory pressure to still be active, so that the object is more likely to be reclaimed.

Point answered 19/7, 2009 at 3:45 Comment(3)
I said almost exactly what Steven Lyons said, only in a different way. How did it not answer the question?Point
Ok. Thanks, now I think you got my point, and I agree with your about using it for completeness.Vainglory
There is no association between an object and the unmanaged memory. See my answer.Aladdin
W
14

It is provided so that the GC knows the true cost of the object during collection. If the object is actually bigger than the managed size reflects, it may be a candidate for quick(er) collection.

Brad Abrams entry about it is pretty clear:

Consider a class that has a very small managed instance size but holds a pointer to a very large chunk of unmanaged memory. Even after no one is referencing the managed instance it could stay alive for a while because the GC sees only the managed instance size it does not think it is “worth it” to free the instance. So we need to “teach” the GC about the true cost of this instance so that it will accurately know when to kick of a collection to free up more memory in the process.

Woodring answered 19/7, 2009 at 3:42 Comment(9)
Actually, now that I re-read your post I my question stands still. After I use my bitmap, I will call dispose on it, so it actually will only take 8 bytes of memory. If in the other hand, I did not call dispose, well, then it really ocuppies 2mb of memory. There's the 3rd case of where there isn't a dispose method, so the GC can do nothing for it. So, the GC knowing the real size of the type will only help in situation 2. Or am I missing something?Vainglory
In case 1, you will want to call GC.AddMemoryPressure on bitmap allocation and then GC.RemoveMemoryPressure when you manually release the bitmap. That will ensure that the object correctly reflects its size to the GC. However, if there is a way to be sure that the GC is never run while your object is eligible for collection and the bitmap is allocated, these methods might not matter.Woodring
Well, in case one, I could do it, but it'd make no big difference, as the GC wouldn't be able to do a thing about my type, if I understand this correctly: 1) I'd declare my variable, 8 managed bytes, 2mb unmanaged bytes. I'd then use it, call dispose, so unmanaged memory is freed. Right now it will only ocuppy 8 bytes. Now, to my eyes, having called in the beggining AddMemoryPressure and RemoveMemoryPressure at the end wouldn't have made anything different. What am I getting wrong? Sorry for being so anoying about this.Vainglory
@Jorge Branco: I think I see your problem. I've edited my original answer to respond to your last comment.Point
I think you understand it fine. The one thing missing from your example is that the GC may initiate a collection because of the memory pressure from your object (though it won't collect the object since it is in use). Other than that, if your objects are always disposed correctly I don't think it will provide you much for those objects. If you don't have control over the usage of your objects, using these APIs would probably be of more use.Woodring
Please tell me where and how GC.AddMemoryPressure() is related to any specific object instance? How will the GC ever know "the true cost of THE object" when there is no relationship?Basile
When the GC runs, it doesn't care how much memory pressure an object has associated with it in when it "decides" whether it's garbage. The only effect of GC pressure is to encourage the GC to run sooner than it otherwise might.Treharne
@Basile you're right. There is no association. See this question and my answer.Aladdin
@Basile if you are working with objects then how about putting ::GC::AddMemoryPressure(n); in the constructor, and RemoveMemoryPressure(n) when cleaning up?Viewfinder
P
10

The point of AddMemoryPressure is to tell the garbage collector that there's a large amount of memory allocated with that object. If it's unmanaged, the garbage collector doesn't know about it; only the managed portion. Since the managed portion is relatively small, the GC may let it pass for garbage collection several times, essentially wasting memory that might need to be freed.

Yes, you still have to manually allocate and deallocate the unmanaged memory. You can't get away from that. You just use AddMemoryPressure to ensure that the GC knows it's there.

Edit:

Well, in case one, I could do it, but it'd make no big difference, as the GC wouldn't be able to do a thing about my type, if I understand this correctly: 1) I'd declare my variable, 8 managed bytes, 2mb unmanaged bytes. I'd then use it, call dispose, so unmanaged memory is freed. Right now it will only ocuppy 8 bytes. Now, to my eyes, having called in the beggining AddMemoryPressure and RemoveMemoryPressure at the end wouldn't have made anything different. What am I getting wrong? Sorry for being so anoying about this. -- Jorge Branco

I think I see your issue.

Yes, if you can guarantee that you always call Dispose, then yes, you don't need to bother with AddMemoryPressure and RemoveMemoryPressure. There is no equivalence, since the reference still exists and the type would never be collected.

That said, you still want to use AddMemoryPressure and RemoveMemoryPressure, for completeness sake. What if, for example, the user of your class forgot to call Dispose? In that case, assuming you implemented the Disposal pattern properly, you'll end up reclaiming your unmanaged bytes at finalization, i.e. when the managed object is collected. In that case, you want the memory pressure to still be active, so that the object is more likely to be reclaimed.

Point answered 19/7, 2009 at 3:45 Comment(3)
I said almost exactly what Steven Lyons said, only in a different way. How did it not answer the question?Point
Ok. Thanks, now I think you got my point, and I agree with your about using it for completeness.Vainglory
There is no association between an object and the unmanaged memory. See my answer.Aladdin
A
7

These methods allow the runtime to have some sense of how much unmanaged memory is being allocated by the process. Without calling these functions, it may not be able to see the true amount of unmanaged memory in use within the process.

However I disagree with the other answers here regarding an association between the memory being referred to and a particular GC object.

Consider:

var buffer = IntPtr.Zero;
try
{
    buffer = Marshal.AllocHGlobal(size);
    GC.AddMemoryPressure(size);

    // ... use buffer ...
}
finally
{
    Marshal.FreeHGlobal(buffer);
    GC.RemoveMemoryPressure(size);
}

There is no way for the GC to assign the size to a specific object. This code could even exist in a static method.

Therefore I assert that the statement we need to “teach” the GC about the true cost of this instance so that it will accurately know when to kick of a collection to free up more memory in the process is incorrect and misleading.

Instead this method might cause the GC to collect earlier than it would otherwise, in an effort to avoid running out of memory.

Aladdin answered 31/5, 2018 at 12:48 Comment(1)
Turns out that an association between an object and unmanaged memory has been questioned before.Aladdin
V
1

Put it this way, still assuming the 8 byte managed objects each referring to a 2 MB unmanaged image. The GC might wait a long time before collecting hundreds or thousands of the little managed objects, because they are so small. That would mean that also hundreds or thousands of linked 2 MB unmanaged chunks will stay alive, awaiting removal. That could become a huge problem. By adding 2 MB of memory pressure in the constructor you will make GC think that the managed object is not 8 bytes big but rather 8 bytes + 2 MB. That will trigger collection way earlier.

Don't forget the Remove call.

Of course if you dispose yourself then you won't need all that.

Viewfinder answered 5/5, 2012 at 20:10 Comment(1)
make GC think that the managed object is not 8 bytes big but rather 8 bytes + 2 MB ...this is not true. There is no association between any object and the unmanaged memory referred to.Aladdin

© 2022 - 2024 — McMap. All rights reserved.