Skip to content

Commit

Permalink
Make the RefreshMemoryLimit API public (#85549)
Browse files Browse the repository at this point in the history
  • Loading branch information
cshung authored May 20, 2023
1 parent d7a30ba commit 5b09cbf
Show file tree
Hide file tree
Showing 15 changed files with 194 additions and 24 deletions.
49 changes: 46 additions & 3 deletions src/coreclr/System.Private.CoreLib/src/System/GC.CoreCLR.cs
Original file line number Diff line number Diff line change
Expand Up @@ -783,7 +783,42 @@ internal enum GCConfigurationType
[LibraryImport(RuntimeHelpers.QCall, EntryPoint = "GCInterface_EnumerateConfigurationValues")]
internal static unsafe partial void _EnumerateConfigurationValues(void* configurationDictionary, delegate* unmanaged<void*, void*, void*, GCConfigurationType, long, void> callback);

private static int _RefreshMemoryLimit()
internal enum RefreshMemoryStatus
{
Succeeded = 0,
HardLimitTooLow = 1,
HardLimitInvalid = 2,
}

/// <summary>
///
/// Instructs the Garbage Collector to reconfigure itself by detecting the various memory limits on the system.
///
/// In addition to actual physical memory limit and container limit settings, these configuration settings can be overwritten:
///
/// - GCHeapHardLimit
/// - GCHeapHardLimitPercent
/// - GCHeapHardLimitSOH
/// - GCHeapHardLimitLOH
/// - GCHeapHardLimitPOH
/// - GCHeapHardLimitSOHPercent
/// - GCHeapHardLimitLOHPercent
/// - GCHeapHardLimitPOHPercent
///
/// Instead of updating the environment variable (which will not be read), these are overridden setting a ulong value in the AppContext.
///
/// For example, you can use AppContext.SetData("GCHeapHardLimit", (ulong) 100 * 1024 * 1024) to override the GCHeapHardLimit to a 100M.
///
/// This API will only handle configs that could be handled when the runtime is loaded, for example, for configs that don't have any effects on 32-bit systems (like the GCHeapHardLimit* ones), this API will not handle it.
///
/// As of now, this API is feature preview only and subject to changes as necessary.
///
/// <exception cref="InvalidOperationException">If the hard limit is too low. This can happen if the heap hard limit that the refresh will set, either because of new AppData settings or implied by the container memory limit changes, is lower than what is already committed.</exception>"
/// <exception cref="InvalidOperationException">If the hard limit is invalid. This can happen, for example, with negative heap hard limit percentages.</exception>"
///
/// </summary>
[System.Runtime.Versioning.RequiresPreviewFeaturesAttribute("RefreshMemoryLimit is in preview.")]
public static void RefreshMemoryLimit()
{
ulong heapHardLimit = (AppContext.GetData("GCHeapHardLimit") as ulong?) ?? ulong.MaxValue;
ulong heapHardLimitPercent = (AppContext.GetData("GCHeapHardLimitPercent") as ulong?) ?? ulong.MaxValue;
Expand All @@ -804,11 +839,19 @@ private static int _RefreshMemoryLimit()
HeapHardLimitLOHPercent = heapHardLimitLOHPercent,
HeapHardLimitPOHPercent = heapHardLimitPOHPercent,
};
return RefreshMemoryLimit(heapHardLimitInfo);
RefreshMemoryStatus status = (RefreshMemoryStatus)_RefreshMemoryLimit(heapHardLimitInfo);
switch (status)
{
case RefreshMemoryStatus.HardLimitTooLow:
throw new InvalidOperationException(SR.InvalidOperationException_HardLimitTooLow);
case RefreshMemoryStatus.HardLimitInvalid:
throw new InvalidOperationException(SR.InvalidOperationException_HardLimitInvalid);
}
Debug.Assert(status == RefreshMemoryStatus.Succeeded);
}

[LibraryImport(RuntimeHelpers.QCall, EntryPoint = "GCInterface_RefreshMemoryLimit")]
internal static partial int RefreshMemoryLimit(GCHeapHardLimitInfo heapHardLimitInfo);
internal static partial int _RefreshMemoryLimit(GCHeapHardLimitInfo heapHardLimitInfo);

internal struct GCHeapHardLimitInfo
{
Expand Down
12 changes: 6 additions & 6 deletions src/coreclr/gc/gc.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -49788,11 +49788,11 @@ bool gc_heap::compute_memory_settings(bool is_initialization, uint32_t& nhp, uin

int gc_heap::refresh_memory_limit()
{
int status = REFRESH_MEMORY_SUCCEED;
refresh_memory_limit_status status = refresh_success;

if (GCConfig::GetGCTotalPhysicalMemory() != 0)
{
return status;
return (int)status;
}

GCToEEInterface::SuspendEE(SUSPEND_FOR_GC);
Expand Down Expand Up @@ -49927,7 +49927,7 @@ int gc_heap::refresh_memory_limit()
if (!compute_hard_limit())
{
succeed = false;
status = REFRESH_MEMORY_HARD_LIMIT_INVALID;
status = refresh_hard_limit_invalid;
}
hard_limit_config_p = heap_hard_limit != 0;
#else
Expand All @@ -49937,7 +49937,7 @@ int gc_heap::refresh_memory_limit()
if (succeed && !compute_memory_settings(false, nhp, nhp_from_config, seg_size_from_config, new_current_total_committed))
{
succeed = false;
status = REFRESH_MEMORY_HARD_LIMIT_TOO_LOW;
status = refresh_hard_limit_too_low;
}

if (!succeed)
Expand Down Expand Up @@ -49969,7 +49969,7 @@ int gc_heap::refresh_memory_limit()
#ifdef COMMITTED_BYTES_SHADOW
assert (new_committed_by_oh[i] == committed_by_oh[i]);
#else
new_committed_by_oh[i] = committed_by_oh[i];
committed_by_oh[i] = new_committed_by_oh[i];
#endif
}
#ifdef MULTIPLE_HEAPS
Expand Down Expand Up @@ -49998,7 +49998,7 @@ int gc_heap::refresh_memory_limit()
#endif
GCToEEInterface::RestartEE(TRUE);

return status;
return (int)status;
}

#ifdef USE_REGIONS
Expand Down
14 changes: 10 additions & 4 deletions src/coreclr/gc/gcinterface.h
Original file line number Diff line number Diff line change
Expand Up @@ -326,6 +326,16 @@ enum end_no_gc_region_status
end_no_gc_alloc_exceeded = 3
};

// !!!!!!!!!!!!!!!!!!!!!!!
// make sure you change the def in bcl\system\gc.cs
// if you change this!
enum refresh_memory_limit_status
{
refresh_success = 0,
refresh_hard_limit_too_low = 1,
refresh_hard_limit_invalid = 2
};

enum gc_kind
{
gc_kind_any = 0, // any of the following kind
Expand Down Expand Up @@ -570,10 +580,6 @@ enum class GCConfigurationType

using ConfigurationValueFunc = void (*)(void* context, void* name, void* publicKey, GCConfigurationType type, int64_t data);

const int REFRESH_MEMORY_SUCCEED = 0;
const int REFRESH_MEMORY_HARD_LIMIT_TOO_LOW = 1;
const int REFRESH_MEMORY_HARD_LIMIT_INVALID = 2;

// IGCHeap is the interface that the VM will use when interacting with the GC.
class IGCHeap {
public:
Expand Down
11 changes: 11 additions & 0 deletions src/coreclr/nativeaot/Runtime/GCHelpers.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -313,6 +313,17 @@ EXTERN_C NATIVEAOT_API void __cdecl RhEnumerateConfigurationValues(void* configu
pHeap->EnumerateConfigurationValues(configurationContext, callback);
}

GCHeapHardLimitInfo g_gcHeapHardLimitInfo;
bool g_gcHeapHardLimitInfoSpecified = false;

EXTERN_C NATIVEAOT_API void __cdecl RhRefreshMemoryLimit(GCHeapHardLimitInfo heapHardLimitInfo)
{
IGCHeap* pHeap = GCHeapUtilities::GetGCHeap();
g_gcHeapHardLimitInfo = heapHardLimitInfo;
g_gcHeapHardLimitInfoSpecified = true;
pHeap->RefreshMemoryLimit();
}

EXTERN_C NATIVEAOT_API int64_t __cdecl RhGetTotalAllocatedBytesPrecise()
{
int64_t allocated;
Expand Down
12 changes: 12 additions & 0 deletions src/coreclr/nativeaot/Runtime/gcenv.h
Original file line number Diff line number Diff line change
Expand Up @@ -97,6 +97,18 @@ typedef DPTR(uint32_t) PTR_uint32_t;

enum CLRDataEnumMemoryFlags : int;

struct GCHeapHardLimitInfo
{
uint64_t heapHardLimit;
uint64_t heapHardLimitPercent;
uint64_t heapHardLimitSOH;
uint64_t heapHardLimitLOH;
uint64_t heapHardLimitPOH;
uint64_t heapHardLimitSOHPercent;
uint64_t heapHardLimitLOHPercent;
uint64_t heapHardLimitPOHPercent;
};

/* _TRUNCATE */
#if !defined (_TRUNCATE)
#define _TRUNCATE ((size_t)-1)
Expand Down
23 changes: 23 additions & 0 deletions src/coreclr/nativeaot/Runtime/gcrhenv.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1374,8 +1374,31 @@ bool GCToEEInterface::GetBooleanConfigValue(const char* privateKey, const char*
return true;
}

extern GCHeapHardLimitInfo g_gcHeapHardLimitInfo;
extern bool g_gcHeapHardLimitInfoSpecified;

bool GCToEEInterface::GetIntConfigValue(const char* privateKey, const char* publicKey, int64_t* value)
{
#ifdef UNICODE
size_t keyLength = strlen(privateKey) + 1;
TCHAR* pKey = (TCHAR*)_alloca(sizeof(TCHAR) * keyLength);
for (size_t i = 0; i < keyLength; i++)
pKey[i] = privateKey[i];
#else
const TCHAR* pKey = privateKey;
#endif
if (g_gcHeapHardLimitInfoSpecified)
{
if ((g_gcHeapHardLimitInfo.heapHardLimit != UINT64_MAX) && strcmp(privateKey, "GCHeapHardLimit") == 0) { *value = g_gcHeapHardLimitInfo.heapHardLimit; return true; }
if ((g_gcHeapHardLimitInfo.heapHardLimitPercent != UINT64_MAX) && strcmp(privateKey, "GCHeapHardLimitPercent") == 0) { *value = g_gcHeapHardLimitInfo.heapHardLimitPercent; return true; }
if ((g_gcHeapHardLimitInfo.heapHardLimitSOH != UINT64_MAX) && strcmp(privateKey, "GCHeapHardLimitSOH") == 0) { *value = g_gcHeapHardLimitInfo.heapHardLimitSOH; return true; }
if ((g_gcHeapHardLimitInfo.heapHardLimitLOH != UINT64_MAX) && strcmp(privateKey, "GCHeapHardLimitLOH") == 0) { *value = g_gcHeapHardLimitInfo.heapHardLimitLOH; return true; }
if ((g_gcHeapHardLimitInfo.heapHardLimitPOH != UINT64_MAX) && strcmp(privateKey, "GCHeapHardLimitPOH") == 0) { *value = g_gcHeapHardLimitInfo.heapHardLimitPOH; return true; }
if ((g_gcHeapHardLimitInfo.heapHardLimitSOHPercent != UINT64_MAX) && strcmp(privateKey, "GCHeapHardLimitSOHPercent") == 0) { *value = g_gcHeapHardLimitInfo.heapHardLimitSOHPercent; return true; }
if ((g_gcHeapHardLimitInfo.heapHardLimitLOHPercent != UINT64_MAX) && strcmp(privateKey, "GCHeapHardLimitLOHPercent") == 0) { *value = g_gcHeapHardLimitInfo.heapHardLimitLOHPercent; return true; }
if ((g_gcHeapHardLimitInfo.heapHardLimitPOHPercent != UINT64_MAX) && strcmp(privateKey, "GCHeapHardLimitPOHPercent") == 0) { *value = g_gcHeapHardLimitInfo.heapHardLimitPOHPercent; return true; }
}

uint64_t uiValue;
if (!g_pRhConfig->ReadConfigValue(privateKey, &uiValue))
return false;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -61,6 +61,13 @@ internal enum EndNoGCRegionStatus
AllocationExceeded = 3
}

internal enum RefreshMemoryStatus
{
Succeeded = 0,
HardLimitTooLow = 1,
HardLimitInvalid = 2,
}

public static partial class GC
{
public static int GetGeneration(object obj)
Expand Down Expand Up @@ -803,5 +810,38 @@ public static TimeSpan GetTotalPauseDuration()
{
return new TimeSpan(RuntimeImports.RhGetTotalPauseDuration());
}

[System.Runtime.Versioning.RequiresPreviewFeaturesAttribute("RefreshMemoryLimit is in preview.")]
public static void RefreshMemoryLimit()
{
ulong heapHardLimit = (AppContext.GetData("GCHeapHardLimit") as ulong?) ?? ulong.MaxValue;
ulong heapHardLimitPercent = (AppContext.GetData("GCHeapHardLimitPercent") as ulong?) ?? ulong.MaxValue;
ulong heapHardLimitSOH = (AppContext.GetData("GCHeapHardLimitSOH") as ulong?) ?? ulong.MaxValue;
ulong heapHardLimitLOH = (AppContext.GetData("GCHeapHardLimitLOH") as ulong?) ?? ulong.MaxValue;
ulong heapHardLimitPOH = (AppContext.GetData("GCHeapHardLimitPOH") as ulong?) ?? ulong.MaxValue;
ulong heapHardLimitSOHPercent = (AppContext.GetData("GCHeapHardLimitSOHPercent") as ulong?) ?? ulong.MaxValue;
ulong heapHardLimitLOHPercent = (AppContext.GetData("GCHeapHardLimitLOHPercent") as ulong?) ?? ulong.MaxValue;
ulong heapHardLimitPOHPercent = (AppContext.GetData("GCHeapHardLimitPOHPercent") as ulong?) ?? ulong.MaxValue;
RuntimeImports.GCHeapHardLimitInfo heapHardLimitInfo = new RuntimeImports.GCHeapHardLimitInfo
{
HeapHardLimit = heapHardLimit,
HeapHardLimitPercent = heapHardLimitPercent,
HeapHardLimitSOH = heapHardLimitSOH,
HeapHardLimitLOH = heapHardLimitLOH,
HeapHardLimitPOH = heapHardLimitPOH,
HeapHardLimitSOHPercent = heapHardLimitSOHPercent,
HeapHardLimitLOHPercent = heapHardLimitLOHPercent,
HeapHardLimitPOHPercent = heapHardLimitPOHPercent,
};
RefreshMemoryStatus status = (RefreshMemoryStatus)RuntimeImports.RhRefreshMemoryLimit(heapHardLimitInfo);
switch (status)
{
case RefreshMemoryStatus.HardLimitTooLow:
throw new InvalidOperationException(SR.InvalidOperationException_HardLimitTooLow);
case RefreshMemoryStatus.HardLimitInvalid:
throw new InvalidOperationException(SR.InvalidOperationException_HardLimitInvalid);
}
Debug.Assert(status == RefreshMemoryStatus.Succeeded);
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -201,6 +201,22 @@ internal enum GCConfigurationType
[UnmanagedCallConv(CallConvs = new Type[] { typeof(CallConvCdecl) })]
internal static unsafe partial void RhEnumerateConfigurationValues(void* configurationContext, delegate* unmanaged<void*, void*, void*, GCConfigurationType, long, void> callback);

internal struct GCHeapHardLimitInfo
{
internal ulong HeapHardLimit;
internal ulong HeapHardLimitPercent;
internal ulong HeapHardLimitSOH;
internal ulong HeapHardLimitLOH;
internal ulong HeapHardLimitPOH;
internal ulong HeapHardLimitSOHPercent;
internal ulong HeapHardLimitLOHPercent;
internal ulong HeapHardLimitPOHPercent;
}

[LibraryImport(RuntimeLibrary)]
[UnmanagedCallConv(CallConvs = new Type[] { typeof(CallConvCdecl) })]
internal static partial int RhRefreshMemoryLimit(GCHeapHardLimitInfo heapHardLimitInfo);

[LibraryImport(RuntimeLibrary)]
[UnmanagedCallConv(CallConvs = new Type[] { typeof(CallConvCdecl) })]
internal static partial long RhGetTotalAllocatedBytesPrecise();
Expand Down
2 changes: 2 additions & 0 deletions src/coreclr/vm/comutilnative.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1211,6 +1211,7 @@ void GCInterface::EnumerateConfigurationValues(void* configurationContext, Enume
}

GCHeapHardLimitInfo g_gcHeapHardLimitInfo;
bool g_gcHeapHardLimitInfoSpecified = false;

extern "C" int QCALLTYPE GCInterface_RefreshMemoryLimit(GCHeapHardLimitInfo heapHardLimitInfo)
{
Expand All @@ -1220,6 +1221,7 @@ extern "C" int QCALLTYPE GCInterface_RefreshMemoryLimit(GCHeapHardLimitInfo heap

BEGIN_QCALL;
g_gcHeapHardLimitInfo = heapHardLimitInfo;
g_gcHeapHardLimitInfoSpecified = true;
result = GCInterface::RefreshMemoryLimit();
END_QCALL;

Expand Down
19 changes: 11 additions & 8 deletions src/coreclr/vm/gcenv.ee.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1191,14 +1191,17 @@ bool GCToEEInterface::GetIntConfigValue(const char* privateKey, const char* publ
return true;
}

if ((g_gcHeapHardLimitInfo.heapHardLimit != UINT64_MAX) && strcmp(privateKey, "GCHeapHardLimit") == 0) { *value = g_gcHeapHardLimitInfo.heapHardLimit; return true; }
if ((g_gcHeapHardLimitInfo.heapHardLimitPercent != UINT64_MAX) && strcmp(privateKey, "GCHeapHardLimitPercent") == 0) { *value = g_gcHeapHardLimitInfo.heapHardLimitPercent; return true; }
if ((g_gcHeapHardLimitInfo.heapHardLimitSOH != UINT64_MAX) && strcmp(privateKey, "GCHeapHardLimitSOH") == 0) { *value = g_gcHeapHardLimitInfo.heapHardLimitSOH; return true; }
if ((g_gcHeapHardLimitInfo.heapHardLimitLOH != UINT64_MAX) && strcmp(privateKey, "GCHeapHardLimitLOH") == 0) { *value = g_gcHeapHardLimitInfo.heapHardLimitLOH; return true; }
if ((g_gcHeapHardLimitInfo.heapHardLimitPOH != UINT64_MAX) && strcmp(privateKey, "GCHeapHardLimitPOH") == 0) { *value = g_gcHeapHardLimitInfo.heapHardLimitPOH; return true; }
if ((g_gcHeapHardLimitInfo.heapHardLimitSOHPercent != UINT64_MAX) && strcmp(privateKey, "GCHeapHardLimitSOHPercent") == 0) { *value = g_gcHeapHardLimitInfo.heapHardLimitSOHPercent; return true; }
if ((g_gcHeapHardLimitInfo.heapHardLimitLOHPercent != UINT64_MAX) && strcmp(privateKey, "GCHeapHardLimitLOHPercent") == 0) { *value = g_gcHeapHardLimitInfo.heapHardLimitLOHPercent; return true; }
if ((g_gcHeapHardLimitInfo.heapHardLimitPOHPercent != UINT64_MAX) && strcmp(privateKey, "GCHeapHardLimitPOHPercent") == 0) { *value = g_gcHeapHardLimitInfo.heapHardLimitPOHPercent; return true; }
if (g_gcHeapHardLimitInfoSpecified)
{
if ((g_gcHeapHardLimitInfo.heapHardLimit != UINT64_MAX) && strcmp(privateKey, "GCHeapHardLimit") == 0) { *value = g_gcHeapHardLimitInfo.heapHardLimit; return true; }
if ((g_gcHeapHardLimitInfo.heapHardLimitPercent != UINT64_MAX) && strcmp(privateKey, "GCHeapHardLimitPercent") == 0) { *value = g_gcHeapHardLimitInfo.heapHardLimitPercent; return true; }
if ((g_gcHeapHardLimitInfo.heapHardLimitSOH != UINT64_MAX) && strcmp(privateKey, "GCHeapHardLimitSOH") == 0) { *value = g_gcHeapHardLimitInfo.heapHardLimitSOH; return true; }
if ((g_gcHeapHardLimitInfo.heapHardLimitLOH != UINT64_MAX) && strcmp(privateKey, "GCHeapHardLimitLOH") == 0) { *value = g_gcHeapHardLimitInfo.heapHardLimitLOH; return true; }
if ((g_gcHeapHardLimitInfo.heapHardLimitPOH != UINT64_MAX) && strcmp(privateKey, "GCHeapHardLimitPOH") == 0) { *value = g_gcHeapHardLimitInfo.heapHardLimitPOH; return true; }
if ((g_gcHeapHardLimitInfo.heapHardLimitSOHPercent != UINT64_MAX) && strcmp(privateKey, "GCHeapHardLimitSOHPercent") == 0) { *value = g_gcHeapHardLimitInfo.heapHardLimitSOHPercent; return true; }
if ((g_gcHeapHardLimitInfo.heapHardLimitLOHPercent != UINT64_MAX) && strcmp(privateKey, "GCHeapHardLimitLOHPercent") == 0) { *value = g_gcHeapHardLimitInfo.heapHardLimitLOHPercent; return true; }
if ((g_gcHeapHardLimitInfo.heapHardLimitPOHPercent != UINT64_MAX) && strcmp(privateKey, "GCHeapHardLimitPOHPercent") == 0) { *value = g_gcHeapHardLimitInfo.heapHardLimitPOHPercent; return true; }
}

WCHAR configKey[MaxConfigKeyLength];
if (MultiByteToWideChar(CP_ACP, 0, privateKey, -1 /* key is null-terminated */, configKey, MaxConfigKeyLength) == 0)
Expand Down
1 change: 1 addition & 0 deletions src/coreclr/vm/gcenv.ee.standalone.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@
extern void FinalizeWeakReference(Object* obj);

extern GCHeapHardLimitInfo g_gcHeapHardLimitInfo;
extern bool g_gcHeapHardLimitInfoSpecified;

namespace standalone
{
Expand Down
1 change: 1 addition & 0 deletions src/coreclr/vm/gcenv.ee.static.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -21,5 +21,6 @@
extern void FinalizeWeakReference(Object* obj);

extern GCHeapHardLimitInfo g_gcHeapHardLimitInfo;
extern bool g_gcHeapHardLimitInfoSpecified;

#include "gcenv.ee.cpp"
Original file line number Diff line number Diff line change
Expand Up @@ -4052,6 +4052,12 @@
<data name="InvalidOperationException_NoGCRegionNotInProgress" xml:space="preserve">
<value>NoGCRegion mode must be set.</value>
</data>
<data name="InvalidOperationException_HardLimitTooLow" xml:space="preserve">
<value>RefreshMemoryLimit failed with too low hard limit.</value>
</data>
<data name="InvalidOperationException_HardLimitInvalid" xml:space="preserve">
<value>RefreshMemoryLimit failed with invalid hard limit.</value>
</data>
<data name="PlatformNotSupported_ReflectionEmit" xml:space="preserve">
<value>Dynamic code generation is not supported on this platform.</value>
</data>
Expand Down
6 changes: 3 additions & 3 deletions src/libraries/System.Runtime/ref/System.Runtime.cs
Original file line number Diff line number Diff line change
Expand Up @@ -2702,10 +2702,10 @@ public static void SuppressFinalize(object obj) { }
public static System.GCNotificationStatus WaitForFullGCComplete(int millisecondsTimeout) { throw null; }
public static System.GCNotificationStatus WaitForFullGCComplete(System.TimeSpan timeout) { throw null; }
public static void WaitForPendingFinalizers() { }

public static TimeSpan GetTotalPauseDuration() { return TimeSpan.Zero; }

public static TimeSpan GetTotalPauseDuration() { throw null; }
public static System.Collections.Generic.IReadOnlyDictionary<string, object> GetConfigurationVariables() { throw null; }
[System.Runtime.Versioning.RequiresPreviewFeaturesAttribute("RefreshMemoryLimit is in preview.")]
public static void RefreshMemoryLimit() { throw null; }
}

public enum GCCollectionMode
Expand Down
6 changes: 6 additions & 0 deletions src/mono/System.Private.CoreLib/src/System/GC.Mono.cs
Original file line number Diff line number Diff line change
Expand Up @@ -321,5 +321,11 @@ public static System.Collections.Generic.IReadOnlyDictionary<string, object> Get
{
return new System.Collections.Generic.Dictionary<string, object>();
}

[System.Runtime.Versioning.RequiresPreviewFeaturesAttribute("RefreshMemoryLimit is in preview.")]
public static void RefreshMemoryLimit()
{
throw new PlatformNotSupportedException();
}
}
}

0 comments on commit 5b09cbf

Please sign in to comment.