NotApplicable = 4
}
+ public readonly struct GCMemoryInfo
+ {
+ /// <summary>
+ /// High memory load threshold when the last GC occured
+ /// </summary>
+ public long HighMemoryLoadThresholdBytes { get; }
+
+ /// <summary>
+ /// Memory load when the last GC ocurred
+ /// </summary>
+ public long MemoryLoadBytes { get; }
+
+ /// <summary>
+ /// 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.
+ /// </summary>
+ public long TotalAvailableMemoryBytes { get; }
+
+ /// <summary>
+ /// The total heap size when the last GC ocurred
+ /// </summary>
+ public long HeapSizeBytes { get; }
+
+ /// <summary>
+ /// 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
+ /// </summary>
+ 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)]
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);
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;
}