| File: src\runtime\src\libraries\System.Private.CoreLib\src\System\GCMemoryInfo.cs | Web Access |
| Project: System.Private.CoreLib.csproj (System.Private.CoreLib) |
// Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. using System.Runtime.CompilerServices; using System.Runtime.InteropServices; namespace System { // !!!!!!!!!!!!!!!!!!!!!!! // make sure you change the def in vm\comutilnative.h // if you change this! // /// <summary> /// Represents the size and the fragmenation of a generation on entry and on exit /// of the GC reported in <see cref="GCMemoryInfo"/>. /// </summary> public readonly struct GCGenerationInfo { /// <summary>Size in bytes on entry to the reported collection.</summary> public long SizeBeforeBytes { get; } /// <summary>Fragmentation in bytes on entry to the reported collection.</summary> public long FragmentationBeforeBytes { get; } /// <summary>Size in bytes on exit from the reported collection.</summary> public long SizeAfterBytes { get; } /// <summary>Fragmentation in bytes on exit from the reported collection.</summary> public long FragmentationAfterBytes { get; } } // !!!!!!!!!!!!!!!!!!!!!!! // make sure you change the def in gc\gcinterface.h // if you change this! // /// <summary>Specifies the kind of a garbage collection.</summary> /// <remarks> /// A GC can be one of the 3 kinds - ephemeral, full blocking or background. /// Their frequencies are very different. Ephemeral GCs happen much more often than /// the other two kinds. Background GCs usually happen infrequently, and /// full blocking GCs usually happen very infrequently. In order to sample the very /// infrequent GCs, collections are separated into kinds so callers can ask for all three kinds while maintaining /// a reasonable sampling rate, e.g. if you are sampling once every second, without this /// distinction, you may never observe a background GC. With this distinction, you can /// always get info of the last GC of the kind you specify. /// </remarks> public enum GCKind { /// <summary>Any kind of collection.</summary> Any = 0, /// <summary>A gen0 or gen1 collection.</summary> Ephemeral = 1, /// <summary>A blocking gen2 collection.</summary> FullBlocking = 2, /// <summary>A background collection.</summary> /// <remarks>This is always a gen2 collection.</remarks> Background = 3 }; [StructLayout(LayoutKind.Sequential)] internal sealed class GCMemoryInfoData { internal long _highMemoryLoadThresholdBytes; internal long _totalAvailableMemoryBytes; internal long _memoryLoadBytes; internal long _heapSizeBytes; internal long _fragmentedBytes; internal long _totalCommittedBytes; internal long _promotedBytes; internal long _pinnedObjectsCount; internal long _finalizationPendingCount; internal long _index; internal int _generation; internal int _pauseTimePercentage; internal byte _compacted; internal byte _concurrent; internal InlineArray5<GCGenerationInfo> _generationInfo; internal InlineArray2<TimeSpan> _pauseDurations; } /// <summary>Provides a set of APIs that can be used to retrieve garbage collection information.</summary> /// <remarks> /// A GC is identified by its Index. which starts from 1 and increases with each GC (see more explanation /// of it in the Index prooperty). /// If you are asking for a GC that does not exist, eg, you called the GC.GetGCMemoryInfo API /// before a GC happened, or you are asking for a GC of GCKind.FullBlocking and no full blocking /// GCs have happened, you will get all 0's in the info, including the Index. So you can use Index 0 /// to detect that no GCs, or no GCs of the kind you specified have happened. /// </remarks> public readonly struct GCMemoryInfo { private readonly GCMemoryInfoData _data; internal GCMemoryInfo(GCMemoryInfoData data) { _data = data; } /// <summary> /// High memory load threshold when this GC occurred /// </summary> public long HighMemoryLoadThresholdBytes => _data._highMemoryLoadThresholdBytes; /// <summary> /// Memory load when this GC occurred /// </summary> public long MemoryLoadBytes => _data._memoryLoadBytes; /// <summary> /// Total available memory for the GC to use when this GC occurred. /// /// If the environment variable DOTNET_GCHeapHardLimit is set, /// or "Server.GC.HeapHardLimit" is in runtimeconfig.json, this will come from that. /// If the program is run in a container, this will be an implementation-defined fraction of the container's size. /// Else, this is the physical memory on the machine that was available for the GC to use when this GC occurred. /// </summary> public long TotalAvailableMemoryBytes => _data._totalAvailableMemoryBytes; /// <summary> /// The total heap size when this GC occurred /// </summary> public long HeapSizeBytes => _data._heapSizeBytes; /// <summary> /// The total fragmentation when this GC occurred /// /// 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 => _data._fragmentedBytes; /// <summary> /// The index of this GC. GC indices start with 1 and get increased at the beginning of a GC. /// Since the info is updated at the end of a GC, this means you can get the info for a BGC /// with a smaller index than a foreground GC finished earlier. /// </summary> public long Index => _data._index; /// <summary> /// The generation this GC collected. Collecting a generation means all its younger generation(s) /// are also collected. /// </summary> public int Generation => _data._generation; /// <summary> /// Is this a compacting GC or not. /// </summary> public bool Compacted => _data._compacted != 0; /// <summary> /// Is this a concurrent GC (BGC) or not. /// </summary> public bool Concurrent => _data._concurrent != 0; /// <summary> /// Total committed bytes of the managed heap. /// </summary> public long TotalCommittedBytes => _data._totalCommittedBytes; /// <summary> /// Promoted bytes for this GC. /// </summary> public long PromotedBytes => _data._promotedBytes; /// <summary> /// Number of pinned objects this GC observed. /// </summary> public long PinnedObjectsCount => _data._pinnedObjectsCount; /// <summary> /// Number of objects ready for finalization this GC observed. /// </summary> public long FinalizationPendingCount => _data._finalizationPendingCount; /// <summary> /// Pause durations. For blocking GCs there's only 1 pause; for BGC there are 2. /// </summary> public ReadOnlySpan<TimeSpan> PauseDurations => _data._pauseDurations; /// <summary> /// This is the % pause time in GC so far. If it's 1.2%, this number is 1.2. /// </summary> public double PauseTimePercentage => (double)_data._pauseTimePercentage / 100.0; /// <summary> /// Generation info for all generations. /// </summary> public ReadOnlySpan<GCGenerationInfo> GenerationInfo => _data._generationInfo; } }