Uh oh!
There was an error while loading. Please reload this page.
[MONO] Implement GC.AddMemoryPressure in Mono - #82121
Conversation
ghost
commented
Feb 14, 2023
Tagging subscribers to this area: @BrzVlad Issue DetailsDRAFT This implements GC.AddMemoryPressure in Mono. It basically just copies the heuristics around this API from the CoreCLR implementation and maps it to sgen: runtime/src/coreclr/vm/comutilnative.cpp Line 1261 in f857468
|
032d071 to
1440afeComparenaricc
commented
Feb 14, 2023
Uh oh!
There was an error while loading. Please reload this page.
Uh oh!
There was an error while loading. Please reload this page.
BrzVlad
commented
Feb 15, 2023
Isn't adding memory pressure the same as if we had an object allocated with that size. And have the same GC heuristic as if there is more memory allocated ? Although in that case, in the long term it would lead for fewer GCs on mono. |
@BrzVlad I expected it to work that way too. But after looking at the CoreCLR implementation, and talking to @AaronRobinsonMSFT, the existing implementation turns out not to do it that way. Its basically a heuristic around GC.Collect, with some information from the user. If the user keeps AddingMemoryPressure, it may force a collection, if it would not add too much GC overhead. But it doesn't actually change the GCs normal calculation. Of course, I could make the mono implementation different. But I elected to follow the existing implementation to start with. |
ea31496 to
c677eaeCompareUh oh!
There was an error while loading. Please reload this page.
Uh oh!
There was an error while loading. Please reload this page.
Uh oh!
There was an error while loading. Please reload this page.
Remove extra define.
12addc6 to
f6235fdCompareUh oh!
There was an error while loading. Please reload this page.
This comment was marked as outdated.
This comment was marked as outdated.
Context: dotnet#1085 Context: dotnet/runtime#82121 Some Java objects are *big*, e.g. [`Bitmap`][0] instances, but as far as MonoVM is concerned, the `Bitmap` instances are *tiny*: a few pointers, and that's it. MonoVM doesn't know that it could be referencing several MB of data in the Java VM. MonoVM is gaining support for [`GC.AddMemoryPressure()`][1] and [`GC.RemoveMemoryPressure()`][2], which potentially allows for the parent of all cross-VM GC integrations: using the `GC` methods to inform MonoVM of how much non-managed memory has been allocated. This could allow MonoVM to collect more frequently when total process memory is low. How should we call `GC.AddMemoryPressure()` and `GC.RemoveMemoryPressure()`? `GC.RemoveMemoryPressure()` is straightforward: a class can override `Java.Lang.Object.Dispose(bool)`. `GC.AddMemoryPressure()` is the problem: where and how can it be called from a class binding? This is trickier, as there was no way to have custom code called by the bound type. Instead, various forms of "hacky workarounds" are often employed, e.g. copying `generator`-emitted content into a `partial` class, then using `metadata` to *prevent* `generator` from binding those members. It's all around fugly. Fortunately C# has a solution: [`partial` methods][3]! We take our existing "peer constructor" generation code, a'la: partial class Bitmap : Java.Lang.Object { internal Bitmap (IntPtr h, JniHandleOwnership t) : base (h, t) { } } and extend it to: partial class Bitmap : Java.Lang.Object { partial void _OnBitmapCreated (); internal Bitmap (IntPtr h, JniHandleOwnership t) : base (h, t) { _OnBitmapCreated (); } } This allows a `partial class Bitmap` to do: // Hand-written code partial class Bitmap { int _memoryPressure; partial void _OnBitmapCreated () { _memoryPressure = ByteCount; GC.AddMemoryPressure (_memoryPressure); } protected override void Dispose (bool disposing) { if (_memoryPressure != 0) { GC.RemoveMemoryPressure (_memoryPressure); _memoryPressure = 0; } } } TODO: Are there any other places where such `partial` methods would be useful? This appears to be the minimum required for this scenario. [0]: https://developer.android.com/reference/android/graphics/Bitmap [1]: https://learn.microsoft.com/dotnet/api/system.gc.addmemorypressure?view=net-7.0 [2]: https://learn.microsoft.com/dotnet/api/system.gc.removememorypressure?view=net-7.0 [3]: https://learn.microsoft.com/dotnet/csharp/language-reference/keywords/partial-method
Context: dotnet#1085 Context: dotnet/runtime#82121 Some Java objects are *big*, e.g. [`Bitmap`][0] instances, but as far as MonoVM is concerned, the `Bitmap` instances are *tiny*: a few pointers, and that's it. MonoVM doesn't know that it could be referencing several MB of data in the Java VM. MonoVM is gaining support for [`GC.AddMemoryPressure()`][1] and [`GC.RemoveMemoryPressure()`][2], which potentially allows for the parent of all cross-VM GC integrations: using the `GC` methods to inform MonoVM of how much non-managed memory has been allocated. This could allow MonoVM to collect more frequently when total process memory is low. How should we call `GC.AddMemoryPressure()` and `GC.RemoveMemoryPressure()`? `GC.RemoveMemoryPressure()` is straightforward: a class can override `Java.Lang.Object.Dispose(bool)`. `GC.AddMemoryPressure()` is the problem: where and how can it be called from a class binding? This is trickier, as there was no way to have custom code called by the bound type. Instead, various forms of "hacky workarounds" are often employed, e.g. copying `generator`-emitted content into a `partial` class, then using `metadata` to *prevent* `generator` from binding those members. It's all around fugly. Fortunately C# has a solution: [`partial` methods][3]! We take our existing "peer constructor" generation code, a'la: partial class Bitmap : Java.Lang.Object { internal Bitmap (IntPtr h, JniHandleOwnership t) : base (h, t) { } } and extend it to: partial class Bitmap : Java.Lang.Object { partial void _OnBitmapCreated (); internal Bitmap (IntPtr h, JniHandleOwnership t) : base (h, t) { _OnBitmapCreated (); } } This allows a `partial class Bitmap` to do: // Hand-written code partial class Bitmap { int _memoryPressure; partial void _OnBitmapCreated () { _memoryPressure = ByteCount; GC.AddMemoryPressure (_memoryPressure); } protected override void Dispose (bool disposing) { if (_memoryPressure != 0) { GC.RemoveMemoryPressure (_memoryPressure); _memoryPressure = 0; } } } TODO: Are there any other places where such `partial` methods would be useful? This appears to be the minimum required for this scenario. [0]: https://developer.android.com/reference/android/graphics/Bitmap [1]: https://learn.microsoft.com/dotnet/api/system.gc.addmemorypressure?view=net-7.0 [2]: https://learn.microsoft.com/dotnet/api/system.gc.removememorypressure?view=net-7.0 [3]: https://learn.microsoft.com/dotnet/csharp/language-reference/keywords/partial-method
This implements GC.AddMemoryPressure in Mono. It basically just copies the heuristics around this API from the CoreCLR implementation and maps it to sgen:
runtime/src/coreclr/vm/comutilnative.cpp
Line 1261 in f857468
Those heuristics may need tuning for mono work loads, but we need some implementation to even begin that work. This seems like a good starting point.
This contributes to missing the missing GC API issue: #73167