From a8d440c432bacf81b770f65042ed1721f38be5e5 Mon Sep 17 00:00:00 2001 From: Ludovic Henry Date: Wed, 10 Apr 2019 15:57:00 -0700 Subject: [PATCH] Make GC.GetGCMemoryInfo public (#23779) * Make GC.GetGCMemoryInfo public This is to be used to allow users to optimize memory consumption based on what's available to the GC and on the system. This is based on https://github.com/dotnet/corefx/issues/34631 * Address reviews - Pass GCMemoryInfo struct to native in place of many arguments - Make GCMemoryInfo.HeapSize and GCMemoryInfo.Fragmentation long in place of IntPtr * Address reviews - Mark GCMemoryInfo readonly * Rearrange fields and match managed/native names * Fix compilation * Use getter shorthand * Address API Design Review https://github.com/dotnet/corefx/issues/34631#issuecomment-481358549 * Fix comments * Fix comments * Do everything in managed * Address review - Remove unecessary [StructLayout.Sequential] - Remove "_" prefix for parameters variables --- .../Buffers/TlsOverPerCoreLockedStacksArrayPool.cs | 6 +- src/System.Private.CoreLib/src/System/GC.cs | 65 ++++++++++++++++++++++ 2 files changed, 68 insertions(+), 3 deletions(-) diff --git a/src/System.Private.CoreLib/shared/System/Buffers/TlsOverPerCoreLockedStacksArrayPool.cs b/src/System.Private.CoreLib/shared/System/Buffers/TlsOverPerCoreLockedStacksArrayPool.cs index 5c5fd99..59e109a 100644 --- a/src/System.Private.CoreLib/shared/System/Buffers/TlsOverPerCoreLockedStacksArrayPool.cs +++ b/src/System.Private.CoreLib/shared/System/Buffers/TlsOverPerCoreLockedStacksArrayPool.cs @@ -301,12 +301,12 @@ namespace System.Buffers const double HighPressureThreshold = .90; // Percent of GC memory pressure threshold we consider "high" const double MediumPressureThreshold = .70; // Percent of GC memory pressure threshold we consider "medium" - GC.GetMemoryInfo(out uint threshold, out _, out uint lastLoad, out _, out _); - if (lastLoad >= threshold * HighPressureThreshold) + GCMemoryInfo memoryInfo = GC.GetGCMemoryInfo(); + if (memoryInfo.MemoryLoadBytes >= memoryInfo.HighMemoryLoadThresholdBytes * HighPressureThreshold) { return MemoryPressure.High; } - else if (lastLoad >= threshold * MediumPressureThreshold) + else if (memoryInfo.MemoryLoadBytes >= memoryInfo.HighMemoryLoadThresholdBytes * MediumPressureThreshold) { return MemoryPressure.Medium; } diff --git a/src/System.Private.CoreLib/src/System/GC.cs b/src/System.Private.CoreLib/src/System/GC.cs index 4485afe..dd043e5 100644 --- a/src/System.Private.CoreLib/src/System/GC.cs +++ b/src/System.Private.CoreLib/src/System/GC.cs @@ -50,6 +50,56 @@ namespace System NotApplicable = 4 } + public readonly struct GCMemoryInfo + { + /// + /// High memory load threshold when the last GC occured + /// + public long HighMemoryLoadThresholdBytes { get; } + + /// + /// Memory load when the last GC ocurred + /// + public long MemoryLoadBytes { get; } + + /// + /// Total available memory for the GC to use when the last GC ocurred. By default this is the physical memory on the machine, but it may be customized by specifying a HardLimit. + /// + public long TotalAvailableMemoryBytes { get; } + + /// + /// The total heap size when the last GC ocurred + /// + public long HeapSizeBytes { get; } + + /// + /// The total fragmentation when the last GC ocurred + /// + /// Let's take the example below: + /// | OBJ_A | OBJ_B | OBJ_C | OBJ_D | OBJ_E | + /// + /// Let's say OBJ_B, OBJ_C and and OBJ_E are garbage and get collected, but the heap does not get compacted, the resulting heap will look like the following: + /// | OBJ_A | F | OBJ_D | + /// + /// The memory between OBJ_A and OBJ_D marked `F` is considered part of the FragmentedBytes, and will be used to allocate new objects. The memory after OBJ_D will not be + /// considered part of the FragmentedBytes, and will also be used to allocate new objects + /// + public long FragmentedBytes { get; } + + internal GCMemoryInfo(long highMemoryLoadThresholdBytes, + long memoryLoadBytes, + long totalAvailableMemoryBytes, + long heapSizeBytes, + long fragmentedBytes) + { + HighMemoryLoadThresholdBytes = highMemoryLoadThresholdBytes; + MemoryLoadBytes = memoryLoadBytes; + TotalAvailableMemoryBytes = totalAvailableMemoryBytes; + HeapSizeBytes = heapSizeBytes; + FragmentedBytes = fragmentedBytes; + } + } + public static class GC { [MethodImplAttribute(MethodImplOptions.InternalCall)] @@ -60,6 +110,21 @@ namespace System out UIntPtr lastRecordedHeapSize, out UIntPtr lastRecordedFragmentation); + public static GCMemoryInfo GetGCMemoryInfo() + { + GetMemoryInfo(out uint highMemLoadThreshold, + out ulong totalPhysicalMem, + out uint lastRecordedMemLoad, + out UIntPtr lastRecordedHeapSize, + out UIntPtr lastRecordedFragmentation); + + return new GCMemoryInfo((long)((double)highMemLoadThreshold / 100 * totalPhysicalMem), + (long)((double)lastRecordedMemLoad / 100 * totalPhysicalMem), + (long)totalPhysicalMem, + (long)(ulong)lastRecordedHeapSize, + (long)(ulong)lastRecordedFragmentation); + } + [DllImport(JitHelpers.QCall, CharSet = CharSet.Unicode)] internal static extern int _StartNoGCRegion(long totalSize, bool lohSizeKnown, long lohSize, bool disallowFullBlockingGC); -- 2.7.4