diff --git a/src/coreclr/System.Private.CoreLib/src/System/GC.CoreCLR.cs b/src/coreclr/System.Private.CoreLib/src/System/GC.CoreCLR.cs
index 184ce80f44b899..0c88b7ada8e0c4 100644
--- a/src/coreclr/System.Private.CoreLib/src/System/GC.CoreCLR.cs
+++ b/src/coreclr/System.Private.CoreLib/src/System/GC.CoreCLR.cs
@@ -60,7 +60,7 @@ public enum GCNotificationStatus
public static partial class GC
{
[MethodImpl(MethodImplOptions.InternalCall)]
- private static extern void GetMemoryInfo(GCMemoryInfoData data, int kind);
+ private static extern unsafe void GetMemoryInfo(GCMemoryInfoData* data, int kind);
/// Gets garbage collection memory information.
/// An object that contains information about the garbage collector's memory usage.
@@ -69,7 +69,7 @@ public static partial class GC
/// Gets garbage collection memory information.
/// The kind of collection for which to retrieve memory information.
/// An object that contains information about the garbage collector's memory usage.
- public static GCMemoryInfo GetGCMemoryInfo(GCKind kind)
+ public static unsafe GCMemoryInfo GetGCMemoryInfo(GCKind kind)
{
if ((kind < GCKind.Any) || (kind > GCKind.Background))
{
@@ -80,8 +80,8 @@ public static GCMemoryInfo GetGCMemoryInfo(GCKind kind)
GCKind.Background));
}
- var data = new GCMemoryInfoData();
- GetMemoryInfo(data, (int)kind);
+ GCMemoryInfoData data = default;
+ GetMemoryInfo(&data, (int)kind);
return new GCMemoryInfo(data);
}
diff --git a/src/coreclr/nativeaot/System.Private.CoreLib/src/System/GC.NativeAot.cs b/src/coreclr/nativeaot/System.Private.CoreLib/src/System/GC.NativeAot.cs
index 67d09f8b246a6d..3905b7f3e74b2d 100644
--- a/src/coreclr/nativeaot/System.Private.CoreLib/src/System/GC.NativeAot.cs
+++ b/src/coreclr/nativeaot/System.Private.CoreLib/src/System/GC.NativeAot.cs
@@ -764,7 +764,7 @@ public static long GetTotalAllocatedBytes(bool precise = false)
/// Gets garbage collection memory information.
/// The kind of collection for which to retrieve memory information.
/// An object that contains information about the garbage collector's memory usage.
- public static GCMemoryInfo GetGCMemoryInfo(GCKind kind)
+ public static unsafe GCMemoryInfo GetGCMemoryInfo(GCKind kind)
{
if ((kind < GCKind.Any) || (kind > GCKind.Background))
{
@@ -775,8 +775,8 @@ public static GCMemoryInfo GetGCMemoryInfo(GCKind kind)
GCKind.Background));
}
- var data = new GCMemoryInfoData();
- RuntimeImports.RhGetMemoryInfo(ref data.GetRawData(), kind);
+ GCMemoryInfoData data = default;
+ RuntimeImports.RhGetMemoryInfo(&data, kind);
return new GCMemoryInfo(data);
}
diff --git a/src/coreclr/nativeaot/System.Private.CoreLib/src/System/Runtime/RuntimeImports.cs b/src/coreclr/nativeaot/System.Private.CoreLib/src/System/Runtime/RuntimeImports.cs
index 4fe7ee2e706106..bf140a03c69a75 100644
--- a/src/coreclr/nativeaot/System.Private.CoreLib/src/System/Runtime/RuntimeImports.cs
+++ b/src/coreclr/nativeaot/System.Private.CoreLib/src/System/Runtime/RuntimeImports.cs
@@ -241,7 +241,7 @@ internal struct GCHeapHardLimitInfo
[MethodImpl(MethodImplOptions.InternalCall)]
[RuntimeImport(RuntimeLibrary, "RhGetMemoryInfo")]
- internal static extern void RhGetMemoryInfo(ref byte info, GCKind kind);
+ internal static extern unsafe void RhGetMemoryInfo(GCMemoryInfoData* data, GCKind kind);
[LibraryImport(RuntimeLibrary)]
internal static unsafe partial void RhAllocateNewArray(MethodTable* pArrayEEType, uint numElements, uint flags, void* pResult);
diff --git a/src/coreclr/vm/comutilnative.cpp b/src/coreclr/vm/comutilnative.cpp
index d039226c328cad..031cb04bff2d9f 100644
--- a/src/coreclr/vm/comutilnative.cpp
+++ b/src/coreclr/vm/comutilnative.cpp
@@ -584,32 +584,30 @@ FCIMPL0(INT64, GCInterface::GetTotalPauseDuration)
}
FCIMPLEND
-FCIMPL2(void, GCInterface::GetMemoryInfo, Object* objUNSAFE, int kind)
+FCIMPL2(void, GCInterface::GetMemoryInfo, GCMemoryInfoData* pData, int kind)
{
FCALL_CONTRACT;
FC_GC_POLL_NOT_NEEDED();
- GCMEMORYINFODATAREF objGCMemoryInfo = (GCMEMORYINFODATAREF)(ObjectToOBJECTREF (objUNSAFE));
-
- UINT64* genInfoRaw = (UINT64*)&(objGCMemoryInfo->generationInfo0);
- UINT64* pauseInfoRaw = (UINT64*)&(objGCMemoryInfo->pauseDuration0);
+ UINT64* genInfoRaw = (UINT64*)&(pData->generationInfo0);
+ UINT64* pauseInfoRaw = (UINT64*)&(pData->pauseDuration0);
return GCHeapUtilities::GetGCHeap()->GetMemoryInfo(
- &(objGCMemoryInfo->highMemLoadThresholdBytes),
- &(objGCMemoryInfo->totalAvailableMemoryBytes),
- &(objGCMemoryInfo->lastRecordedMemLoadBytes),
- &(objGCMemoryInfo->lastRecordedHeapSizeBytes),
- &(objGCMemoryInfo->lastRecordedFragmentationBytes),
- &(objGCMemoryInfo->totalCommittedBytes),
- &(objGCMemoryInfo->promotedBytes),
- &(objGCMemoryInfo->pinnedObjectCount),
- &(objGCMemoryInfo->finalizationPendingCount),
- &(objGCMemoryInfo->index),
- &(objGCMemoryInfo->generation),
- &(objGCMemoryInfo->pauseTimePercent),
- (bool*)&(objGCMemoryInfo->isCompaction),
- (bool*)&(objGCMemoryInfo->isConcurrent),
+ &(pData->highMemLoadThresholdBytes),
+ &(pData->totalAvailableMemoryBytes),
+ &(pData->lastRecordedMemLoadBytes),
+ &(pData->lastRecordedHeapSizeBytes),
+ &(pData->lastRecordedFragmentationBytes),
+ &(pData->totalCommittedBytes),
+ &(pData->promotedBytes),
+ &(pData->pinnedObjectCount),
+ &(pData->finalizationPendingCount),
+ &(pData->index),
+ &(pData->generation),
+ &(pData->pauseTimePercent),
+ (bool*)&(pData->isCompaction),
+ (bool*)&(pData->isConcurrent),
genInfoRaw,
pauseInfoRaw,
kind);
diff --git a/src/coreclr/vm/comutilnative.h b/src/coreclr/vm/comutilnative.h
index 1f5fda9ed02ccc..cf8cc472622dae 100644
--- a/src/coreclr/vm/comutilnative.h
+++ b/src/coreclr/vm/comutilnative.h
@@ -101,7 +101,7 @@ struct GCGenerationInfo
};
#include "pshpack4.h"
-class GCMemoryInfoData : public Object
+struct GCMemoryInfoData
{
public:
UINT64 highMemLoadThresholdBytes;
@@ -131,14 +131,6 @@ class GCMemoryInfoData : public Object
};
#include "poppack.h"
-#ifdef USE_CHECKED_OBJECTREFS
-typedef REF GCMEMORYINFODATA;
-typedef REF GCMEMORYINFODATAREF;
-#else // USE_CHECKED_OBJECTREFS
-typedef GCMemoryInfoData * GCMEMORYINFODATA;
-typedef GCMemoryInfoData * GCMEMORYINFODATAREF;
-#endif // USE_CHECKED_OBJECTREFS
-
using EnumerateConfigurationValuesCallback = void (*)(void* context, void* name, void* publicKey, GCConfigurationType type, int64_t data);
struct GCHeapHardLimitInfo
@@ -166,7 +158,7 @@ class GCInterface {
static FORCEINLINE UINT64 InterlockedSub(UINT64 *pMinuend, UINT64 subtrahend);
static FCDECL0(INT64, GetTotalPauseDuration);
- static FCDECL2(void, GetMemoryInfo, Object* objUNSAFE, int kind);
+ static FCDECL2(void, GetMemoryInfo, GCMemoryInfoData* pData, int kind);
static FCDECL0(UINT32, GetMemoryLoad);
static FCDECL0(int, GetGcLatencyMode);
static FCDECL1(int, SetGcLatencyMode, int newLatencyMode);
diff --git a/src/libraries/System.Private.CoreLib/src/System/Buffers/SharedArrayPool.cs b/src/libraries/System.Private.CoreLib/src/System/Buffers/SharedArrayPool.cs
index 89f32fe337f161..7a91aef50cf3f5 100644
--- a/src/libraries/System.Private.CoreLib/src/System/Buffers/SharedArrayPool.cs
+++ b/src/libraries/System.Private.CoreLib/src/System/Buffers/SharedArrayPool.cs
@@ -209,14 +209,14 @@ public bool Trim()
{
if (!log.IsEnabled())
{
- foreach (KeyValuePair tlsBuckets in _allTlsBuckets)
+ foreach (KeyValuePair tlsBuckets in _allTlsBuckets.EnumerateOnStack())
{
Array.Clear(tlsBuckets.Key);
}
}
else
{
- foreach (KeyValuePair tlsBuckets in _allTlsBuckets)
+ foreach (KeyValuePair tlsBuckets in _allTlsBuckets.EnumerateOnStack())
{
SharedArrayPoolThreadLocalArray[] buckets = tlsBuckets.Key;
for (int i = 0; i < buckets.Length; i++)
@@ -241,7 +241,7 @@ public bool Trim()
_ => 30_000,
};
- foreach (KeyValuePair tlsBuckets in _allTlsBuckets)
+ foreach (KeyValuePair tlsBuckets in _allTlsBuckets.EnumerateOnStack())
{
SharedArrayPoolThreadLocalArray[] buckets = tlsBuckets.Key;
for (int i = 0; i < buckets.Length; i++)
diff --git a/src/libraries/System.Private.CoreLib/src/System/GCMemoryInfo.cs b/src/libraries/System.Private.CoreLib/src/System/GCMemoryInfo.cs
index 59cce34d7d4038..0b1eba089cd1ca 100644
--- a/src/libraries/System.Private.CoreLib/src/System/GCMemoryInfo.cs
+++ b/src/libraries/System.Private.CoreLib/src/System/GCMemoryInfo.cs
@@ -54,7 +54,7 @@ public enum GCKind
};
[StructLayout(LayoutKind.Sequential)]
- internal sealed class GCMemoryInfoData
+ internal struct GCMemoryInfoData
{
internal long _highMemoryLoadThresholdBytes;
internal long _totalAvailableMemoryBytes;
diff --git a/src/libraries/System.Private.CoreLib/src/System/Runtime/CompilerServices/ConditionalWeakTable.cs b/src/libraries/System.Private.CoreLib/src/System/Runtime/CompilerServices/ConditionalWeakTable.cs
index eb6af90b5a5043..1dfd43f9c01e5a 100644
--- a/src/libraries/System.Private.CoreLib/src/System/Runtime/CompilerServices/ConditionalWeakTable.cs
+++ b/src/libraries/System.Private.CoreLib/src/System/Runtime/CompilerServices/ConditionalWeakTable.cs
@@ -369,8 +369,8 @@ IEnumerator> IEnumerable>.
IEnumerator IEnumerable.GetEnumerator() => ((IEnumerable>)this).GetEnumerator();
- /// Provides an enumerator for the table.
- private sealed class Enumerator : IEnumerator>
+ /// The core implementation for the enumerators for the table.
+ private struct EnumeratorCore : IDisposable
{
// The enumerator would ideally hold a reference to the Container and the end index within that
// container. However, the safety of the CWT depends on the only reference to the Container being
@@ -393,7 +393,7 @@ private sealed class Enumerator : IEnumerator>
private int _currentIndex; // the current index into the container
private KeyValuePair _current; // the current entry set by MoveNext and returned from Current
- public Enumerator(ConditionalWeakTable table)
+ public EnumeratorCore(ConditionalWeakTable table)
{
Debug.Assert(table != null, "Must provide a valid table");
Debug.Assert(Monitor.IsEntered(table._lock), "Must hold the _lock lock to construct the enumerator");
@@ -410,11 +410,6 @@ public Enumerator(ConditionalWeakTable table)
_currentIndex = -1;
}
- ~Enumerator()
- {
- Dispose();
- }
-
public void Dispose()
{
// Use an interlocked operation to ensure that only one thread can get access to
@@ -485,6 +480,56 @@ public KeyValuePair Current
return _current;
}
}
+ }
+
+ /// Provides an enumerator for the table.
+ private sealed class Enumerator : IEnumerator>
+ {
+ private EnumeratorCore _enumeratorCore;
+
+ public Enumerator(ConditionalWeakTable table) => _enumeratorCore = new(table);
+
+ ~Enumerator()
+ {
+ Dispose();
+ }
+
+ public void Dispose() => _enumeratorCore.Dispose();
+
+ public bool MoveNext() => _enumeratorCore.MoveNext();
+
+ public KeyValuePair Current => _enumeratorCore.Current;
+
+ object? IEnumerator.Current => Current;
+
+ public void Reset() { }
+ }
+
+ /// Gets an enumerable used to enumerate the values on the stack without allocation.
+ // This API is unsafe to use if the user neglects to call Dispose, which is why
+ // we can only use this internally where we can enforce proper foreach usage.
+ // It's also unsafe to use in async functions, even with proper foreach, because there's
+ // a chance that the async will never continue, in which case we need to rely on the finalizer in the regular enumerator.
+ internal RefEnumerable EnumerateOnStack() => new RefEnumerable(this);
+
+ /// Provides an enumerable used to enumerate the values on the stack without allocation.
+ internal readonly ref struct RefEnumerable(ConditionalWeakTable table)
+ {
+ public RefEnumerator GetEnumerator() => new(table);
+ }
+
+ /// Provides an enumerator for the table that can be used on the stack without allocation.
+ internal ref struct RefEnumerator : IEnumerator>
+ {
+ private EnumeratorCore _enumeratorCore;
+
+ public RefEnumerator(ConditionalWeakTable table) => _enumeratorCore = new(table);
+
+ public void Dispose() => _enumeratorCore.Dispose();
+
+ public bool MoveNext() => _enumeratorCore.MoveNext();
+
+ public KeyValuePair Current => _enumeratorCore.Current;
object? IEnumerator.Current => Current;
diff --git a/src/mono/System.Private.CoreLib/src/System/GC.Mono.cs b/src/mono/System.Private.CoreLib/src/System/GC.Mono.cs
index 21eba8223fd0f9..ce69362d8a397d 100644
--- a/src/mono/System.Private.CoreLib/src/System/GC.Mono.cs
+++ b/src/mono/System.Private.CoreLib/src/System/GC.Mono.cs
@@ -248,7 +248,7 @@ private static extern void _GetGCMemoryInfo(out long highMemoryLoadThresholdByte
public static GCMemoryInfo GetGCMemoryInfo()
{
- var data = new GCMemoryInfoData();
+ GCMemoryInfoData data = default;
_GetGCMemoryInfo(out data._highMemoryLoadThresholdBytes,
out data._memoryLoadBytes,