Skip to content

Commit 41c1c10

Browse files
authored
Aggressive GC (#69695)
1 parent 549b431 commit 41c1c10

File tree

13 files changed

+217
-12
lines changed

13 files changed

+217
-12
lines changed

src/coreclr/System.Private.CoreLib/src/System/GC.CoreCLR.cs

Lines changed: 20 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,8 @@ public enum GCCollectionMode
2323
{
2424
Default = 0,
2525
Forced = 1,
26-
Optimized = 2
26+
Optimized = 2,
27+
Aggressive = 3,
2728
}
2829

2930
// !!!!!!!!!!!!!!!!!!!!!!!
@@ -35,6 +36,7 @@ internal enum InternalGCCollectionMode
3536
Blocking = 0x00000002,
3637
Optimized = 0x00000004,
3738
Compacting = 0x00000008,
39+
Aggressive = 0x00000010,
3840
}
3941

4042
// !!!!!!!!!!!!!!!!!!!!!!!
@@ -197,7 +199,7 @@ public static void Collect(int generation, GCCollectionMode mode, bool blocking,
197199
throw new ArgumentOutOfRangeException(nameof(generation), SR.ArgumentOutOfRange_GenericPositive);
198200
}
199201

200-
if ((mode < GCCollectionMode.Default) || (mode > GCCollectionMode.Optimized))
202+
if ((mode < GCCollectionMode.Default) || (mode > GCCollectionMode.Aggressive))
201203
{
202204
throw new ArgumentOutOfRangeException(nameof(mode), SR.ArgumentOutOfRange_Enum);
203205
}
@@ -209,6 +211,22 @@ public static void Collect(int generation, GCCollectionMode mode, bool blocking,
209211
{
210212
iInternalModes |= (int)InternalGCCollectionMode.Optimized;
211213
}
214+
else if (mode == GCCollectionMode.Aggressive)
215+
{
216+
iInternalModes |= (int)InternalGCCollectionMode.Aggressive;
217+
if (generation != MaxGeneration)
218+
{
219+
throw new ArgumentException(SR.Argument_AggressiveGCRequiresMaxGeneration, nameof(generation));
220+
}
221+
if (!blocking)
222+
{
223+
throw new ArgumentException(SR.Argument_AggressiveGCRequiresBlocking, nameof(blocking));
224+
}
225+
if (!compacting)
226+
{
227+
throw new ArgumentException(SR.Argument_AggressiveGCRequiresCompacting, nameof(compacting));
228+
}
229+
}
212230

213231
if (compacting)
214232
iInternalModes |= (int)InternalGCCollectionMode.Compacting;

src/coreclr/gc/gc.cpp

Lines changed: 68 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -211,6 +211,7 @@ BOOL is_induced (gc_reason reason)
211211
(reason == reason_lowmemory) ||
212212
(reason == reason_lowmemory_blocking) ||
213213
(reason == reason_induced_compacting) ||
214+
(reason == reason_induced_aggressive) ||
214215
(reason == reason_lowmemory_host) ||
215216
(reason == reason_lowmemory_host_blocking));
216217
}
@@ -221,6 +222,7 @@ BOOL is_induced_blocking (gc_reason reason)
221222
return ((reason == reason_induced) ||
222223
(reason == reason_lowmemory_blocking) ||
223224
(reason == reason_induced_compacting) ||
225+
(reason == reason_induced_aggressive) ||
224226
(reason == reason_lowmemory_host_blocking));
225227
}
226228

@@ -12297,6 +12299,56 @@ void gc_heap::distribute_free_regions()
1229712299
{
1229812300
#ifdef USE_REGIONS
1229912301
const int kind_count = large_free_region + 1;
12302+
if (settings.reason == reason_induced_aggressive)
12303+
{
12304+
#ifdef MULTIPLE_HEAPS
12305+
for (int i = 0; i < n_heaps; i++)
12306+
{
12307+
gc_heap* hp = g_heaps[i];
12308+
#else //MULTIPLE_HEAPS
12309+
{
12310+
gc_heap* hp = pGenGCHeap;
12311+
#endif //MULTIPLE_HEAPS
12312+
for (int kind = basic_free_region; kind < count_free_region_kinds; kind++)
12313+
{
12314+
global_regions_to_decommit[kind].transfer_regions (&hp->free_regions[kind]);
12315+
}
12316+
}
12317+
while (decommit_step())
12318+
{
12319+
}
12320+
#ifdef MULTIPLE_HEAPS
12321+
for (int i = 0; i < n_heaps; i++)
12322+
{
12323+
gc_heap* hp = g_heaps[i];
12324+
int hn = i;
12325+
#else //MULTIPLE_HEAPS
12326+
{
12327+
gc_heap* hp = pGenGCHeap;
12328+
int hn = 0;
12329+
#endif //MULTIPLE_HEAPS
12330+
for (int i = 0; i < total_generation_count; i++)
12331+
{
12332+
generation* generation = hp->generation_of (i);
12333+
heap_segment* region = heap_segment_rw (generation_start_segment (generation));
12334+
while (region != nullptr)
12335+
{
12336+
uint8_t* aligned_allocated = align_on_page (heap_segment_allocated (region));
12337+
size_t end_space = heap_segment_committed (region) - aligned_allocated;
12338+
if (end_space > 0)
12339+
{
12340+
virtual_decommit (aligned_allocated, end_space, gen_to_oh (i), hn);
12341+
heap_segment_committed (region) = aligned_allocated;
12342+
heap_segment_used (region) = min (heap_segment_used (region), heap_segment_committed (region));
12343+
assert (heap_segment_committed (region) > heap_segment_mem (region));
12344+
}
12345+
region = heap_segment_next_rw (region);
12346+
}
12347+
}
12348+
}
12349+
12350+
return;
12351+
}
1230012352

1230112353
// first step: accumulate the number of free regions and the budget over all heaps
1230212354
// and move huge regions to global free list
@@ -30568,6 +30620,10 @@ void gc_heap::update_start_tail_regions (generation* gen,
3056830620
inline
3056930621
bool gc_heap::should_sweep_in_plan (heap_segment* region)
3057030622
{
30623+
if (settings.reason == reason_induced_aggressive)
30624+
{
30625+
return false;
30626+
}
3057130627
bool sip_p = false;
3057230628
int gen_num = get_region_gen_num (region);
3057330629
int new_gen_num = get_plan_gen_num (gen_num);
@@ -40218,6 +40274,13 @@ BOOL gc_heap::decide_on_compacting (int condemned_gen_number,
4021840274
get_gc_data_per_heap()->set_mechanism (gc_heap_compact, compact_induced_compacting);
4021940275
}
4022040276

40277+
if (settings.reason == reason_induced_aggressive)
40278+
{
40279+
dprintf (2, ("aggressive compacting GC"));
40280+
should_compact = TRUE;
40281+
get_gc_data_per_heap()->set_mechanism (gc_heap_compact, compact_aggressive_compacting);
40282+
}
40283+
4022140284
if (settings.reason == reason_pm_full_gc)
4022240285
{
4022340286
assert (condemned_gen_number == max_generation);
@@ -45232,7 +45295,11 @@ GCHeap::GarbageCollectTry (int generation, BOOL low_memory_p, int mode)
4523245295

4523345296
if (reason == reason_induced)
4523445297
{
45235-
if (mode & collection_compacting)
45298+
if (mode & collection_aggressive)
45299+
{
45300+
reason = reason_induced_aggressive;
45301+
}
45302+
else if (mode & collection_compacting)
4523645303
{
4523745304
reason = reason_induced_compacting;
4523845305
}

src/coreclr/gc/gc.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -74,6 +74,7 @@ enum gc_reason
7474
reason_bgc_tuning_soh = 14,
7575
reason_bgc_tuning_loh = 15,
7676
reason_bgc_stepping = 16,
77+
reason_induced_aggressive = 17,
7778
reason_max
7879
};
7980

src/coreclr/gc/gcinterface.h

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -275,7 +275,8 @@ enum collection_mode
275275
collection_non_blocking = 0x00000001,
276276
collection_blocking = 0x00000002,
277277
collection_optimized = 0x00000004,
278-
collection_compacting = 0x00000008
278+
collection_compacting = 0x00000008,
279+
collection_aggressive = 0x00000010
279280
#ifdef STRESS_HEAP
280281
, collection_gcstress = 0x80000000
281282
#endif // STRESS_HEAP

src/coreclr/gc/gcrecord.h

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -248,7 +248,8 @@ enum gc_heap_compact_reason
248248
compact_high_mem_frag = 8,
249249
compact_vhigh_mem_frag = 9,
250250
compact_no_gc_mode = 10,
251-
max_compact_reasons_count = 11
251+
compact_aggressive_compacting = 11,
252+
max_compact_reasons_count = 12
252253
};
253254

254255
#ifndef DACCESS_COMPILE
@@ -264,7 +265,8 @@ static BOOL gc_heap_compact_reason_mandatory_p[] =
264265
FALSE, //compact_high_mem_load = 7,
265266
TRUE, //compact_high_mem_frag = 8,
266267
TRUE, //compact_vhigh_mem_frag = 9,
267-
TRUE //compact_no_gc_mode = 10
268+
TRUE, //compact_no_gc_mode = 10,
269+
TRUE //compact_aggressive_compacting = 11
268270
};
269271

270272
static BOOL gc_expand_mechanism_mandatory_p[] =

src/coreclr/nativeaot/System.Private.CoreLib/src/System/GC.NativeAot.cs

Lines changed: 20 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,8 @@ public enum GCCollectionMode
2222
{
2323
Default = 0,
2424
Forced = 1,
25-
Optimized = 2
25+
Optimized = 2,
26+
Aggressive = 3,
2627
}
2728

2829
public enum GCNotificationStatus
@@ -40,6 +41,7 @@ internal enum InternalGCCollectionMode
4041
Blocking = 0x00000002,
4142
Optimized = 0x00000004,
4243
Compacting = 0x00000008,
44+
Aggressive = 0x00000010
4345
}
4446

4547
internal enum StartNoGCRegionStatus
@@ -124,7 +126,7 @@ public static void Collect(int generation, GCCollectionMode mode, bool blocking,
124126
throw new ArgumentOutOfRangeException(nameof(generation), SR.ArgumentOutOfRange_GenericPositive);
125127
}
126128

127-
if ((mode < GCCollectionMode.Default) || (mode > GCCollectionMode.Optimized))
129+
if ((mode < GCCollectionMode.Default) || (mode > GCCollectionMode.Aggressive))
128130
{
129131
throw new ArgumentOutOfRangeException(nameof(mode), SR.ArgumentOutOfRange_Enum);
130132
}
@@ -135,6 +137,22 @@ public static void Collect(int generation, GCCollectionMode mode, bool blocking,
135137
{
136138
iInternalModes |= (int)InternalGCCollectionMode.Optimized;
137139
}
140+
else if (mode == GCCollectionMode.Aggressive)
141+
{
142+
iInternalModes |= (int)InternalGCCollectionMode.Aggressive;
143+
if (generation != MaxGeneration)
144+
{
145+
throw new ArgumentException(SR.Argument_AggressiveGCRequiresMaxGeneration, nameof(generation));
146+
}
147+
if (!blocking)
148+
{
149+
throw new ArgumentException(SR.Argument_AggressiveGCRequiresBlocking, nameof(blocking));
150+
}
151+
if (!compacting)
152+
{
153+
throw new ArgumentException(SR.Argument_AggressiveGCRequiresCompacting, nameof(compacting));
154+
}
155+
}
138156

139157
if (compacting)
140158
{

src/libraries/System.Private.CoreLib/src/Resources/Strings.resx

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3529,6 +3529,15 @@
35293529
<data name="Arg_NullArgumentNullRef" xml:space="preserve">
35303530
<value>The method was called with a null array argument.</value>
35313531
</data>
3532+
<data name="Argument_AggressiveGCRequiresMaxGeneration" xml:space="preserve">
3533+
<value>AggressiveGC requires setting the generation parameter to MaxGeneration</value>
3534+
</data>
3535+
<data name="Argument_AggressiveGCRequiresBlocking" xml:space="preserve">
3536+
<value>AggressiveGC requires setting the blocking parameter to true.</value>
3537+
</data>
3538+
<data name="Argument_AggressiveGCRequiresCompacting" xml:space="preserve">
3539+
<value>AggressiveGC requires setting the compacting parameter to true.</value>
3540+
</data>
35323541
<data name="Argument_CannotPrepareAbstract" xml:space="preserve">
35333542
<value>Abstract methods cannot be prepared.</value>
35343543
</data>

src/libraries/System.Runtime/ref/System.Runtime.cs

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2610,12 +2610,23 @@ public static void WaitForPendingFinalizers() { }
26102610
/// <returns> The total amount of time paused in GC since the beginning of the process.</returns>
26112611
public static TimeSpan GetTotalPauseDuration() { return TimeSpan.Zero; }
26122612
}
2613+
2614+
/// <summary>Specifies the behavior for a forced garbage collection.</summary>
26132615
public enum GCCollectionMode
26142616
{
2617+
/// <summary>The default setting for this enumeration, which is currently <see cref="GCCollectionMode.Forced" />.</summary>
26152618
Default = 0,
2619+
2620+
/// <summary>Forces the garbage collection to occur immediately.</summary>
26162621
Forced = 1,
2622+
2623+
/// <summary>Allows the garbage collector to determine whether the current time is optimal to reclaim objects.</summary>
26172624
Optimized = 2,
2625+
2626+
/// <summary>Requests that the garbage collector decommit as much memory as possible.</summary>
2627+
Aggressive = 3,
26182628
}
2629+
26192630
public readonly partial struct GCGenerationInfo
26202631
{
26212632
private readonly int _dummyPrimitive;

src/libraries/System.Runtime/tests/System/GCTests.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -69,7 +69,7 @@ public static void Collect_NegativeGenerationCount_ThrowsArgumentOutOfRangeExcep
6969

7070
[Theory]
7171
[InlineData(GCCollectionMode.Default - 1)]
72-
[InlineData(GCCollectionMode.Optimized + 1)]
72+
[InlineData(GCCollectionMode.Aggressive + 1)]
7373
public static void Collection_InvalidCollectionMode_ThrowsArgumentOutOfRangeException(GCCollectionMode mode)
7474
{
7575
AssertExtensions.Throws<ArgumentOutOfRangeException>("mode", null, () => GC.Collect(2, mode));

src/mono/System.Private.CoreLib/src/System/GC.Mono.cs

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,8 @@ public enum GCCollectionMode
1111
{
1212
Default = 0,
1313
Forced = 1,
14-
Optimized = 2
14+
Optimized = 2,
15+
Aggressive = 3,
1516
}
1617

1718
public enum GCNotificationStatus
@@ -91,7 +92,7 @@ public static void Collect(int generation, GCCollectionMode mode, bool blocking,
9192
{
9293
if (generation < 0)
9394
throw new ArgumentOutOfRangeException(nameof(generation), "generation", SR.ArgumentOutOfRange_GenericPositive);
94-
if ((mode < GCCollectionMode.Default) || (mode > GCCollectionMode.Optimized))
95+
if ((mode < GCCollectionMode.Default) || (mode > GCCollectionMode.Aggressive))
9596
throw new ArgumentOutOfRangeException(nameof(mode), SR.ArgumentOutOfRange_Enum);
9697

9798
InternalCollect(generation);
Lines changed: 62 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,62 @@
1+
// Licensed to the .NET Foundation under one or more agreements.
2+
// The .NET Foundation licenses this file to you under the MIT license.
3+
4+
using System;
5+
using System.Diagnostics;
6+
using System.Runtime.CompilerServices;
7+
8+
public class AggressiveCollect
9+
{
10+
public static int Main(string[] args )
11+
{
12+
long before = CreateGarbage();
13+
GC.Collect(2, GCCollectionMode.Aggressive, blocking: true, compacting: true);
14+
long after = GC.GetGCMemoryInfo().TotalCommittedBytes;
15+
long reclaimed = before - after;
16+
long reclaimedAtLeast = 2000 * 4000;
17+
if (reclaimed < reclaimedAtLeast)
18+
{
19+
// If we reach this case, the aggressive GC is not releasing as much memory as
20+
// we wished, something is wrong.
21+
return 101;
22+
}
23+
else
24+
{
25+
// Doing some extra allocation (and also trigger GC indirectly) here
26+
// should be just fine.
27+
for (int i = 0; i < 10; i++)
28+
{
29+
CreateGarbage();
30+
}
31+
return 100;
32+
}
33+
}
34+
35+
[MethodImpl(MethodImplOptions.NoInlining)]
36+
public static long CreateGarbage()
37+
{
38+
byte[][] smallGarbage = new byte[2000][];
39+
40+
// This will force us to use more than one region in the small object heap
41+
for (int i = 0; i < 2000; i++)
42+
{
43+
// It will roughly span one page
44+
smallGarbage[i] = new byte[4000];
45+
}
46+
47+
// This will force us to use more than one region in the large object heap
48+
byte[] largeGarbage = new byte[33 * 1024 * 1024];
49+
50+
// This will force us to use more than one region in the pin object heap
51+
byte[] pinnedGarbage = GC.AllocateArray<byte>(33 * 1024 * 1024, /* pinned = */true);
52+
53+
GC.Collect(2, GCCollectionMode.Forced, blocking: true, compacting: true);
54+
long committed = GC.GetGCMemoryInfo().TotalCommittedBytes;
55+
56+
GC.KeepAlive(smallGarbage);
57+
GC.KeepAlive(largeGarbage);
58+
GC.KeepAlive(pinnedGarbage);
59+
60+
return committed;
61+
}
62+
}
Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
<Project Sdk="Microsoft.NET.Sdk">
2+
<PropertyGroup>
3+
<OutputType>Exe</OutputType>
4+
<!-- Consider enable it for Mono whenever the implementation is ready -->
5+
<CLRTestTargetUnsupported Condition="'$(RuntimeFlavor)' != 'coreclr'">true</CLRTestTargetUnsupported>
6+
<CLRTestPriority>0</CLRTestPriority>
7+
</PropertyGroup>
8+
<PropertyGroup>
9+
<!-- Set to 'Full' if the Debug? column is marked in the spreadsheet. Leave blank otherwise. -->
10+
<DebugType>PdbOnly</DebugType>
11+
</PropertyGroup>
12+
<ItemGroup>
13+
<Compile Include="Collect_Aggressive.cs" />
14+
</ItemGroup>
15+
</Project>

0 commit comments

Comments
 (0)