runtime: OutOfMemory: NativeHeapMemoryBlock should call GC.AddMemoryPressure.
I believe that calling Marshal.AllocHGlobal() without calling GC.AddMemoryPressure (and subsequently GC.RemoveMemoryPressure when releasing the memory) is a mistake.
See https://github.com/dotnet/roslyn/issues/24939 for a strong example of why we care about this and why it can lead to OutOfMemory exceptions.
About this issue
- Original URL
- State: closed
- Created 4 years ago
- Comments: 15 (12 by maintainers)
If it helps, the implementation of the Add/RemoveMemoryPressure API is here: https://github.com/dotnet/runtime/blob/c0ddd1c5d1636de873398c8d9544e02289f95fec/src/coreclr/src/vm/comutilnative.cpp#L1282
Notice that it is not actually built into the GC. It is a simple control loop with a few statics: Cumulative counter of the total memory pressure, and a threshold.
I agree that the docs for this can be better.
I have submitted doc update as https://github.com/dotnet/dotnet-api-docs/pull/4812
@jkotas, I thought for large allocations you wanted both? That is you want
IDisposableto ensure the resource is freed properly without relying on the finalizer, but that won’t inform the GC that you allocated 3GB of native memory while that memory lives (especially if it is long lived). So the pattern would be essentially:If that isn’t the case, maybe the documentation (https://docs.microsoft.com/en-us/dotnet/api/system.gc.addmemorypressure?view=netframework-4.8) could be improved to better detail when you might want to use the API and provide an example of how to use it properly. CC. @Maoni0
It is not a mistake. 99.99+% code that allocates native memory does not call AddMemoryPressure / RemoveMemoryPressure and it is just fine.
The GC does query the overall state of the process and runs more aggressively when it sees that the memory is getting tight.
Also, the best practice is to use IDisposable to explicitly dispose expensive unmanaged resources that includes large unmanaged memory blocks. AddMemoryPressure / RemoveMemoryPressure does not help much in this case.
AddMemoryPressure / RemoveMemoryPressure helps for cases where the unmanaged resources are disposed via finalizers. It was originally introduced for built-in COM interop that typically does this.