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 d6474dcc8c83d3..45d8a5762e23df 100644
--- a/src/coreclr/System.Private.CoreLib/src/System/GC.CoreCLR.cs
+++ b/src/coreclr/System.Private.CoreLib/src/System/GC.CoreCLR.cs
@@ -23,7 +23,8 @@ public enum GCCollectionMode
{
Default = 0,
Forced = 1,
- Optimized = 2
+ Optimized = 2,
+ Aggressive = 3,
}
// !!!!!!!!!!!!!!!!!!!!!!!
@@ -35,6 +36,7 @@ internal enum InternalGCCollectionMode
Blocking = 0x00000002,
Optimized = 0x00000004,
Compacting = 0x00000008,
+ Aggressive = 0x00000010,
}
// !!!!!!!!!!!!!!!!!!!!!!!
@@ -197,7 +199,7 @@ public static void Collect(int generation, GCCollectionMode mode, bool blocking,
throw new ArgumentOutOfRangeException(nameof(generation), SR.ArgumentOutOfRange_GenericPositive);
}
- if ((mode < GCCollectionMode.Default) || (mode > GCCollectionMode.Optimized))
+ if ((mode < GCCollectionMode.Default) || (mode > GCCollectionMode.Aggressive))
{
throw new ArgumentOutOfRangeException(nameof(mode), SR.ArgumentOutOfRange_Enum);
}
@@ -209,6 +211,22 @@ public static void Collect(int generation, GCCollectionMode mode, bool blocking,
{
iInternalModes |= (int)InternalGCCollectionMode.Optimized;
}
+ else if (mode == GCCollectionMode.Aggressive)
+ {
+ iInternalModes |= (int)InternalGCCollectionMode.Aggressive;
+ if (generation != MaxGeneration)
+ {
+ throw new ArgumentException(SR.Argument_AggressiveGCRequiresMaxGeneration, nameof(generation));
+ }
+ if (!blocking)
+ {
+ throw new ArgumentException(SR.Argument_AggressiveGCRequiresBlocking, nameof(blocking));
+ }
+ if (!compacting)
+ {
+ throw new ArgumentException(SR.Argument_AggressiveGCRequiresCompacting, nameof(compacting));
+ }
+ }
if (compacting)
iInternalModes |= (int)InternalGCCollectionMode.Compacting;
diff --git a/src/coreclr/gc/gc.cpp b/src/coreclr/gc/gc.cpp
index e42d29365f7225..a7cccb0d2d88bd 100644
--- a/src/coreclr/gc/gc.cpp
+++ b/src/coreclr/gc/gc.cpp
@@ -211,6 +211,7 @@ BOOL is_induced (gc_reason reason)
(reason == reason_lowmemory) ||
(reason == reason_lowmemory_blocking) ||
(reason == reason_induced_compacting) ||
+ (reason == reason_induced_aggressive) ||
(reason == reason_lowmemory_host) ||
(reason == reason_lowmemory_host_blocking));
}
@@ -221,6 +222,7 @@ BOOL is_induced_blocking (gc_reason reason)
return ((reason == reason_induced) ||
(reason == reason_lowmemory_blocking) ||
(reason == reason_induced_compacting) ||
+ (reason == reason_induced_aggressive) ||
(reason == reason_lowmemory_host_blocking));
}
@@ -12297,6 +12299,56 @@ void gc_heap::distribute_free_regions()
{
#ifdef USE_REGIONS
const int kind_count = large_free_region + 1;
+ if (settings.reason == reason_induced_aggressive)
+ {
+#ifdef MULTIPLE_HEAPS
+ for (int i = 0; i < n_heaps; i++)
+ {
+ gc_heap* hp = g_heaps[i];
+#else //MULTIPLE_HEAPS
+ {
+ gc_heap* hp = pGenGCHeap;
+#endif //MULTIPLE_HEAPS
+ for (int kind = basic_free_region; kind < count_free_region_kinds; kind++)
+ {
+ global_regions_to_decommit[kind].transfer_regions (&hp->free_regions[kind]);
+ }
+ }
+ while (decommit_step())
+ {
+ }
+#ifdef MULTIPLE_HEAPS
+ for (int i = 0; i < n_heaps; i++)
+ {
+ gc_heap* hp = g_heaps[i];
+ int hn = i;
+#else //MULTIPLE_HEAPS
+ {
+ gc_heap* hp = pGenGCHeap;
+ int hn = 0;
+#endif //MULTIPLE_HEAPS
+ for (int i = 0; i < total_generation_count; i++)
+ {
+ generation* generation = hp->generation_of (i);
+ heap_segment* region = heap_segment_rw (generation_start_segment (generation));
+ while (region != nullptr)
+ {
+ uint8_t* aligned_allocated = align_on_page (heap_segment_allocated (region));
+ size_t end_space = heap_segment_committed (region) - aligned_allocated;
+ if (end_space > 0)
+ {
+ virtual_decommit (aligned_allocated, end_space, gen_to_oh (i), hn);
+ heap_segment_committed (region) = aligned_allocated;
+ heap_segment_used (region) = min (heap_segment_used (region), heap_segment_committed (region));
+ assert (heap_segment_committed (region) > heap_segment_mem (region));
+ }
+ region = heap_segment_next_rw (region);
+ }
+ }
+ }
+
+ return;
+ }
// first step: accumulate the number of free regions and the budget over all heaps
// and move huge regions to global free list
@@ -30568,6 +30620,10 @@ void gc_heap::update_start_tail_regions (generation* gen,
inline
bool gc_heap::should_sweep_in_plan (heap_segment* region)
{
+ if (settings.reason == reason_induced_aggressive)
+ {
+ return false;
+ }
bool sip_p = false;
int gen_num = get_region_gen_num (region);
int new_gen_num = get_plan_gen_num (gen_num);
@@ -40218,6 +40274,13 @@ BOOL gc_heap::decide_on_compacting (int condemned_gen_number,
get_gc_data_per_heap()->set_mechanism (gc_heap_compact, compact_induced_compacting);
}
+ if (settings.reason == reason_induced_aggressive)
+ {
+ dprintf (2, ("aggressive compacting GC"));
+ should_compact = TRUE;
+ get_gc_data_per_heap()->set_mechanism (gc_heap_compact, compact_aggressive_compacting);
+ }
+
if (settings.reason == reason_pm_full_gc)
{
assert (condemned_gen_number == max_generation);
@@ -45233,7 +45296,11 @@ GCHeap::GarbageCollectTry (int generation, BOOL low_memory_p, int mode)
if (reason == reason_induced)
{
- if (mode & collection_compacting)
+ if (mode & collection_aggressive)
+ {
+ reason = reason_induced_aggressive;
+ }
+ else if (mode & collection_compacting)
{
reason = reason_induced_compacting;
}
diff --git a/src/coreclr/gc/gc.h b/src/coreclr/gc/gc.h
index a47f84089bc431..b3d771bd34fafd 100644
--- a/src/coreclr/gc/gc.h
+++ b/src/coreclr/gc/gc.h
@@ -74,6 +74,7 @@ enum gc_reason
reason_bgc_tuning_soh = 14,
reason_bgc_tuning_loh = 15,
reason_bgc_stepping = 16,
+ reason_induced_aggressive = 17,
reason_max
};
diff --git a/src/coreclr/gc/gcinterface.h b/src/coreclr/gc/gcinterface.h
index a1edb39e59fcb3..669be562ca05cb 100644
--- a/src/coreclr/gc/gcinterface.h
+++ b/src/coreclr/gc/gcinterface.h
@@ -275,7 +275,8 @@ enum collection_mode
collection_non_blocking = 0x00000001,
collection_blocking = 0x00000002,
collection_optimized = 0x00000004,
- collection_compacting = 0x00000008
+ collection_compacting = 0x00000008,
+ collection_aggressive = 0x00000010
#ifdef STRESS_HEAP
, collection_gcstress = 0x80000000
#endif // STRESS_HEAP
diff --git a/src/coreclr/gc/gcrecord.h b/src/coreclr/gc/gcrecord.h
index 68d0f4dfabe210..7df8d0aca03d77 100644
--- a/src/coreclr/gc/gcrecord.h
+++ b/src/coreclr/gc/gcrecord.h
@@ -248,7 +248,8 @@ enum gc_heap_compact_reason
compact_high_mem_frag = 8,
compact_vhigh_mem_frag = 9,
compact_no_gc_mode = 10,
- max_compact_reasons_count = 11
+ compact_aggressive_compacting = 11,
+ max_compact_reasons_count = 12
};
#ifndef DACCESS_COMPILE
@@ -264,7 +265,8 @@ static BOOL gc_heap_compact_reason_mandatory_p[] =
FALSE, //compact_high_mem_load = 7,
TRUE, //compact_high_mem_frag = 8,
TRUE, //compact_vhigh_mem_frag = 9,
- TRUE //compact_no_gc_mode = 10
+ TRUE, //compact_no_gc_mode = 10,
+ TRUE //compact_aggressive_compacting = 11
};
static BOOL gc_expand_mechanism_mandatory_p[] =
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 b4d59af8f3655b..8ac6a38324d56e 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
@@ -22,7 +22,8 @@ public enum GCCollectionMode
{
Default = 0,
Forced = 1,
- Optimized = 2
+ Optimized = 2,
+ Aggressive = 3,
}
public enum GCNotificationStatus
@@ -40,6 +41,7 @@ internal enum InternalGCCollectionMode
Blocking = 0x00000002,
Optimized = 0x00000004,
Compacting = 0x00000008,
+ Aggressive = 0x00000010
}
internal enum StartNoGCRegionStatus
@@ -124,7 +126,7 @@ public static void Collect(int generation, GCCollectionMode mode, bool blocking,
throw new ArgumentOutOfRangeException(nameof(generation), SR.ArgumentOutOfRange_GenericPositive);
}
- if ((mode < GCCollectionMode.Default) || (mode > GCCollectionMode.Optimized))
+ if ((mode < GCCollectionMode.Default) || (mode > GCCollectionMode.Aggressive))
{
throw new ArgumentOutOfRangeException(nameof(mode), SR.ArgumentOutOfRange_Enum);
}
@@ -135,6 +137,22 @@ public static void Collect(int generation, GCCollectionMode mode, bool blocking,
{
iInternalModes |= (int)InternalGCCollectionMode.Optimized;
}
+ else if (mode == GCCollectionMode.Aggressive)
+ {
+ iInternalModes |= (int)InternalGCCollectionMode.Aggressive;
+ if (generation != MaxGeneration)
+ {
+ throw new ArgumentException(SR.Argument_AggressiveGCRequiresMaxGeneration, nameof(generation));
+ }
+ if (!blocking)
+ {
+ throw new ArgumentException(SR.Argument_AggressiveGCRequiresBlocking, nameof(blocking));
+ }
+ if (!compacting)
+ {
+ throw new ArgumentException(SR.Argument_AggressiveGCRequiresCompacting, nameof(compacting));
+ }
+ }
if (compacting)
{
diff --git a/src/libraries/System.Private.CoreLib/src/Resources/Strings.resx b/src/libraries/System.Private.CoreLib/src/Resources/Strings.resx
index b1deeb8c264849..98c61f7eb80539 100644
--- a/src/libraries/System.Private.CoreLib/src/Resources/Strings.resx
+++ b/src/libraries/System.Private.CoreLib/src/Resources/Strings.resx
@@ -3529,6 +3529,15 @@
The method was called with a null array argument.
+
+ AggressiveGC requires setting the generation parameter to MaxGeneration
+
+
+ AggressiveGC requires setting the blocking parameter to true.
+
+
+ AggressiveGC requires setting the compacting parameter to true.
+
Abstract methods cannot be prepared.
diff --git a/src/libraries/System.Runtime/ref/System.Runtime.cs b/src/libraries/System.Runtime/ref/System.Runtime.cs
index bcfaa53f8fa3fa..5cb7053223fd72 100644
--- a/src/libraries/System.Runtime/ref/System.Runtime.cs
+++ b/src/libraries/System.Runtime/ref/System.Runtime.cs
@@ -2562,12 +2562,23 @@ public static void WaitForPendingFinalizers() { }
/// The total amount of time paused in GC since the beginning of the process.
public static TimeSpan GetTotalPauseDuration() { return TimeSpan.Zero; }
}
+
+ /// Specifies the behavior for a forced garbage collection.
public enum GCCollectionMode
{
+ /// The default setting for this enumeration, which is currently .
Default = 0,
+
+ /// Forces the garbage collection to occur immediately.
Forced = 1,
+
+ /// Allows the garbage collector to determine whether the current time is optimal to reclaim objects.
Optimized = 2,
+
+ /// Requests that the garbage collector decommit as much memory as possible.
+ Aggressive = 3,
}
+
public readonly partial struct GCGenerationInfo
{
private readonly int _dummyPrimitive;
diff --git a/src/libraries/System.Runtime/tests/System/GCTests.cs b/src/libraries/System.Runtime/tests/System/GCTests.cs
index 4faaa1d94e6ea4..31663d2276468b 100644
--- a/src/libraries/System.Runtime/tests/System/GCTests.cs
+++ b/src/libraries/System.Runtime/tests/System/GCTests.cs
@@ -69,7 +69,7 @@ public static void Collect_NegativeGenerationCount_ThrowsArgumentOutOfRangeExcep
[Theory]
[InlineData(GCCollectionMode.Default - 1)]
- [InlineData(GCCollectionMode.Optimized + 1)]
+ [InlineData(GCCollectionMode.Aggressive + 1)]
public static void Collection_InvalidCollectionMode_ThrowsArgumentOutOfRangeException(GCCollectionMode mode)
{
AssertExtensions.Throws("mode", null, () => GC.Collect(2, mode));
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 b5bc27542b5fef..681a8cc03bbaa4 100644
--- a/src/mono/System.Private.CoreLib/src/System/GC.Mono.cs
+++ b/src/mono/System.Private.CoreLib/src/System/GC.Mono.cs
@@ -11,7 +11,8 @@ public enum GCCollectionMode
{
Default = 0,
Forced = 1,
- Optimized = 2
+ Optimized = 2,
+ Aggressive = 3,
}
public enum GCNotificationStatus
@@ -91,7 +92,7 @@ public static void Collect(int generation, GCCollectionMode mode, bool blocking,
{
if (generation < 0)
throw new ArgumentOutOfRangeException(nameof(generation), "generation", SR.ArgumentOutOfRange_GenericPositive);
- if ((mode < GCCollectionMode.Default) || (mode > GCCollectionMode.Optimized))
+ if ((mode < GCCollectionMode.Default) || (mode > GCCollectionMode.Aggressive))
throw new ArgumentOutOfRangeException(nameof(mode), SR.ArgumentOutOfRange_Enum);
InternalCollect(generation);
diff --git a/src/tests/GC/API/GC/Collect_Aggressive.cs b/src/tests/GC/API/GC/Collect_Aggressive.cs
new file mode 100644
index 00000000000000..3d04a8c93d6830
--- /dev/null
+++ b/src/tests/GC/API/GC/Collect_Aggressive.cs
@@ -0,0 +1,62 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+
+using System;
+using System.Diagnostics;
+using System.Runtime.CompilerServices;
+
+public class AggressiveCollect
+{
+ public static int Main(string[] args )
+ {
+ long before = CreateGarbage();
+ GC.Collect(2, GCCollectionMode.Aggressive, blocking: true, compacting: true);
+ long after = GC.GetGCMemoryInfo().TotalCommittedBytes;
+ long reclaimed = before - after;
+ long reclaimedAtLeast = 2000 * 4000;
+ if (reclaimed < reclaimedAtLeast)
+ {
+ // If we reach this case, the aggressive GC is not releasing as much memory as
+ // we wished, something is wrong.
+ return 101;
+ }
+ else
+ {
+ // Doing some extra allocation (and also trigger GC indirectly) here
+ // should be just fine.
+ for (int i = 0; i < 10; i++)
+ {
+ CreateGarbage();
+ }
+ return 100;
+ }
+ }
+
+ [MethodImpl(MethodImplOptions.NoInlining)]
+ public static long CreateGarbage()
+ {
+ byte[][] smallGarbage = new byte[2000][];
+
+ // This will force us to use more than one region in the small object heap
+ for (int i = 0; i < 2000; i++)
+ {
+ // It will roughly span one page
+ smallGarbage[i] = new byte[4000];
+ }
+
+ // This will force us to use more than one region in the large object heap
+ byte[] largeGarbage = new byte[33 * 1024 * 1024];
+
+ // This will force us to use more than one region in the pin object heap
+ byte[] pinnedGarbage = GC.AllocateArray(33 * 1024 * 1024, /* pinned = */true);
+
+ GC.Collect(2, GCCollectionMode.Forced, blocking: true, compacting: true);
+ long committed = GC.GetGCMemoryInfo().TotalCommittedBytes;
+
+ GC.KeepAlive(smallGarbage);
+ GC.KeepAlive(largeGarbage);
+ GC.KeepAlive(pinnedGarbage);
+
+ return committed;
+ }
+}
diff --git a/src/tests/GC/API/GC/Collect_Aggressive.csproj b/src/tests/GC/API/GC/Collect_Aggressive.csproj
new file mode 100644
index 00000000000000..306065392ef021
--- /dev/null
+++ b/src/tests/GC/API/GC/Collect_Aggressive.csproj
@@ -0,0 +1,15 @@
+
+
+ Exe
+
+ true
+ 0
+
+
+
+ PdbOnly
+
+
+
+
+
diff --git a/src/tests/GC/API/GC/Collect_neg.cs b/src/tests/GC/API/GC/Collect_neg.cs
index 06c47ea7adf0e9..979d53016b33bf 100644
--- a/src/tests/GC/API/GC/Collect_neg.cs
+++ b/src/tests/GC/API/GC/Collect_neg.cs
@@ -8,7 +8,7 @@ public class NegCollect
public static int Main()
{
bool retVal = true;
- GCCollectionMode[] invalidInputs = { (GCCollectionMode)(GCCollectionMode.Default - 1), (GCCollectionMode)(GCCollectionMode.Optimized + 1) };
+ GCCollectionMode[] invalidInputs = { (GCCollectionMode)(GCCollectionMode.Default - 1), (GCCollectionMode)(GCCollectionMode.Aggressive + 1) };
for (int i = 0; i < invalidInputs.Length; i++)
{