Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
9 changes: 6 additions & 3 deletions src/mono/System.Private.CoreLib/src/System/GC.Mono.cs
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,10 @@ public static partial class GC
private static extern void InternalCollect(int generation);

[MethodImplAttribute(MethodImplOptions.InternalCall)]
private static extern void RecordPressure(long bytesAllocated);
private static extern void AddPressure(ulong bytesAllocated);

[MethodImplAttribute(MethodImplOptions.InternalCall)]
private static extern void RemovePressure(ulong bytesRemoved);

// TODO: Move following to ConditionalWeakTable
[MethodImplAttribute(MethodImplOptions.InternalCall)]
Expand All @@ -60,7 +63,7 @@ public static void AddMemoryPressure(long bytesAllocated)
{
ArgumentOutOfRangeException.ThrowIfGreaterThan(bytesAllocated, int.MaxValue);
}
RecordPressure(bytesAllocated);
AddPressure((ulong)bytesAllocated);
}

public static void RemoveMemoryPressure(long bytesAllocated)
Expand All @@ -70,7 +73,7 @@ public static void RemoveMemoryPressure(long bytesAllocated)
{
ArgumentOutOfRangeException.ThrowIfGreaterThan(bytesAllocated, int.MaxValue);
}
RecordPressure(-bytesAllocated);
RemovePressure((ulong)bytesAllocated);
}

[MethodImplAttribute(MethodImplOptions.InternalCall)]
Expand Down
12 changes: 9 additions & 3 deletions src/mono/mono/metadata/boehm-gc.c
Original file line number Diff line number Diff line change
Expand Up @@ -311,14 +311,20 @@ mono_restart_world (MonoThreadInfoFlags flags)
* are indirectly referenced by managed objects (for example unmanaged
* memory holding image or other binary data).
* This is a hint only to the garbage collector algorithm.
* Note that negative amounts of p value will decrease the memory
* pressure.
* `value` must be greater than or equal to 0.
* To remove pressure, use `mono_gc_remove_memory_pressure`.
*/
void
mono_gc_add_memory_pressure (gint64 value)
mono_gc_add_memory_pressure (guint64 value)
{
}

void
mono_gc_remove_memory_pressure (guint64 value)
{
}


/**
* mono_gc_get_used_size:
*
Expand Down
4 changes: 3 additions & 1 deletion src/mono/mono/metadata/gc-internals.h
Original file line number Diff line number Diff line change
Expand Up @@ -169,7 +169,9 @@ mono_gc_register_object_with_weak_fields (MonoObjectHandle obj);
typedef void (*MonoFinalizationProc)(gpointer, gpointer); // same as SGenFinalizationProc, GC_finalization_proc

void mono_gc_register_for_finalization (MonoObject *obj, MonoFinalizationProc user_data);
void mono_gc_add_memory_pressure (gint64 value);
void mono_gc_add_memory_pressure (guint64 value);
void mono_gc_remove_memory_pressure (guint64 value);

MONO_API int mono_gc_register_root (char *start, size_t size, MonoGCDescriptor descr, MonoGCRootSource source, void *key, const char *msg);
MONO_COMPONENT_API void mono_gc_deregister_root (char* addr);
void mono_gc_finalize_domain (MonoDomain *domain);
Expand Down
4 changes: 3 additions & 1 deletion src/mono/mono/metadata/icall-decl.h
Original file line number Diff line number Diff line change
Expand Up @@ -131,7 +131,9 @@ ICALL_EXPORT void ves_icall_System_Array_SetGenericValue_icall (MonoObjectHandle

ICALL_EXPORT void ves_icall_System_Environment_Exit (int);
ICALL_EXPORT void ves_icall_System_GC_InternalCollect (int generation);
ICALL_EXPORT void ves_icall_System_GC_RecordPressure (gint64);
ICALL_EXPORT void ves_icall_System_GC_AddPressure (guint64);
ICALL_EXPORT void ves_icall_System_GC_RemovePressure (guint64);

ICALL_EXPORT void ves_icall_System_GC_WaitForPendingFinalizers (void);
ICALL_EXPORT void ves_icall_System_GC_GetGCMemoryInfo (gint64*, gint64*, gint64*, gint64*, gint64*, gint64*);

Expand Down
5 changes: 3 additions & 2 deletions src/mono/mono/metadata/icall-def.h
Original file line number Diff line number Diff line change
Expand Up @@ -206,7 +206,8 @@ NOHANDLES(ICALL(ENV_15, "get_TickCount", ves_icall_System_Environment_get_TickCo
NOHANDLES(ICALL(ENV_15a, "get_TickCount64", ves_icall_System_Environment_get_TickCount64))
NOHANDLES(ICALL(ENV_20, "set_ExitCode", mono_environment_exitcode_set))

ICALL_TYPE(GC, "System.GC", GC_13)
ICALL_TYPE(GC, "System.GC", GC_4a)
NOHANDLES(ICALL(GC_4a, "AddPressure", ves_icall_System_GC_AddPressure))
HANDLES(GC_13, "AllocPinnedArray", ves_icall_System_GC_AllocPinnedArray, MonoArray, 2, (MonoReflectionType, gint32))
NOHANDLES(ICALL(GC_10, "GetAllocatedBytesForCurrentThread", ves_icall_System_GC_GetAllocatedBytesForCurrentThread))
NOHANDLES(ICALL(GC_0, "GetCollectionCount", ves_icall_System_GC_GetCollectionCount))
Expand All @@ -215,7 +216,7 @@ NOHANDLES(ICALL(GC_0b, "GetMaxGeneration", ves_icall_System_GC_GetMaxGeneration)
HANDLES(GC_11, "GetTotalAllocatedBytes", ves_icall_System_GC_GetTotalAllocatedBytes, guint64, 1, (MonoBoolean))
NOHANDLES(ICALL(GC_1, "GetTotalMemory", ves_icall_System_GC_GetTotalMemory))
NOHANDLES(ICALL(GC_2, "InternalCollect", ves_icall_System_GC_InternalCollect))
NOHANDLES(ICALL(GC_4a, "RecordPressure", ves_icall_System_GC_RecordPressure))
NOHANDLES(ICALL(GC_5, "RemovePressure", ves_icall_System_GC_RemovePressure))
NOHANDLES(ICALL(GC_6, "WaitForPendingFinalizers", ves_icall_System_GC_WaitForPendingFinalizers))
NOHANDLES(ICALL(GC_12, "_GetGCMemoryInfo", ves_icall_System_GC_GetGCMemoryInfo))
HANDLES(GC_6b, "_ReRegisterForFinalize", ves_icall_System_GC_ReRegisterForFinalize, void, 1, (MonoObject))
Expand Down
8 changes: 7 additions & 1 deletion src/mono/mono/metadata/icall.c
Original file line number Diff line number Diff line change
Expand Up @@ -7286,11 +7286,17 @@ ves_icall_System_GC_GetTotalAllocatedBytes (MonoBoolean precise, MonoError* erro
}

void
ves_icall_System_GC_RecordPressure (gint64 value)
ves_icall_System_GC_AddPressure (guint64 value)
{
mono_gc_add_memory_pressure (value);
}

void
ves_icall_System_GC_RemovePressure (guint64 value)
{
mono_gc_remove_memory_pressure (value);
}

MonoBoolean
ves_icall_System_Threading_Thread_YieldInternal (void)
{
Expand Down
11 changes: 8 additions & 3 deletions src/mono/mono/metadata/sgen-mono.c
Original file line number Diff line number Diff line change
Expand Up @@ -2867,11 +2867,16 @@ mono_gc_card_table_nursery_check (void)
return !sgen_get_major_collector ()->is_concurrent;
}

/* Negative value to remove */
void
mono_gc_add_memory_pressure (gint64 value)
mono_gc_add_memory_pressure (guint64 value)
{
/* FIXME: Implement at some point? */
sgen_add_memory_pressure(value);
}

void
mono_gc_remove_memory_pressure (guint64 value)
{
sgen_remove_memory_pressure(value);
}

/*
Expand Down
100 changes: 100 additions & 0 deletions src/mono/mono/sgen/sgen-gc.c
Original file line number Diff line number Diff line change
Expand Up @@ -4083,4 +4083,104 @@ sgen_check_canary_for_object (gpointer addr)
}
}


#define MEM_PRESSURE_COUNT 4
#define MAX_MEMORYPRESSURE_RATIO 10
guint64 memory_pressure_gc_count = 0;
guint64 memory_pressure_iteration = 0;
guint64 memory_pressure_adds[MEM_PRESSURE_COUNT] = {0, 0, 0, 0};
guint64 memory_pressure_removes[MEM_PRESSURE_COUNT] = {0, 0, 0, 0}; // history of memory pressure removals
const unsigned min_memorypressure_budget = 4 * 1024 * 1024; // 4 MB

// Resets pressure accounting after a gen2 GC has occurred.
static void check_pressure_counts ()
{
if (memory_pressure_gc_count != sgen_gc_collection_count(GENERATION_OLD)) {
memory_pressure_gc_count = sgen_gc_collection_count(GENERATION_OLD);
mono_atomic_inc_i64((gint64*)&memory_pressure_iteration);

guint32 p = memory_pressure_iteration % MEM_PRESSURE_COUNT;

memory_pressure_adds[p] = 0; // new pressure will be accumulated here
memory_pressure_removes[p] = 0;
}
}

void sgen_remove_memory_pressure (guint64 bytes_allocated)
{
check_pressure_counts();

guint32 p = memory_pressure_iteration % MEM_PRESSURE_COUNT;

mono_atomic_fetch_add_i64((gint64*)&memory_pressure_removes[p], bytes_allocated);
}


static gboolean pressure_check_heuristic (guint64 new_mem_value)
{

guint64 add = 0;
guint64 rem = 0;

guint p = memory_pressure_iteration % MEM_PRESSURE_COUNT;

for (gint i = 0; i < MEM_PRESSURE_COUNT; i++) {
add += memory_pressure_adds[i];
rem += memory_pressure_removes[i];
}

add -= memory_pressure_adds[p];
rem -= memory_pressure_removes[p];

if (new_mem_value >= min_memorypressure_budget) {
guint64 budget = min_memorypressure_budget;

if (memory_pressure_iteration >= MEM_PRESSURE_COUNT) { // wait until we have enough data points
// Adjust according to effectiveness of GC
// Scale budget according to past memory_pressure_adds / memory_pressure_removes ratio
if (add >= rem * MAX_MEMORYPRESSURE_RATIO)
{
budget = min_memorypressure_budget * MAX_MEMORYPRESSURE_RATIO;
}
else if (add > rem)
{
g_assert (rem != 0);
// Avoid overflow by calculating addPressure / remPressure as fixed point (1 = 1024)
budget = (add * 1024 / rem) * budget / 1024;
}
}

// If still over budget, check current managed heap size
if (new_mem_value >= budget) {
guint64 heap_over_3 = sgen_gc_info.heap_size_bytes / 3;

if (budget < heap_over_3) {
budget = heap_over_3;
}
if (new_mem_value >= budget) {
// last check - if we would exceed 20% of GC "duty cycle", do not trigger GC at this time
if ((size_t)(mono_time_since_last_stw() + time_last) > (time_last * 5)) {
return TRUE;
}
}
}
}

return FALSE;
}

void sgen_add_memory_pressure (guint64 bytes_allocated)
{

check_pressure_counts ();

guint p = memory_pressure_iteration % MEM_PRESSURE_COUNT;

guint64 new_mem_value = mono_atomic_fetch_add_i64 ((gint64*)&memory_pressure_adds[p], bytes_allocated);

if (pressure_check_heuristic(new_mem_value)) {
sgen_gc_collect (GENERATION_OLD);
check_pressure_counts ();
}
}
#endif /* HAVE_SGEN_GC */
3 changes: 3 additions & 0 deletions src/mono/mono/sgen/sgen-gc.h
Original file line number Diff line number Diff line change
Expand Up @@ -1230,6 +1230,9 @@ sgen_dummy_use (gpointer v)
#endif
}

void sgen_add_memory_pressure(guint64 value);
void sgen_remove_memory_pressure(guint64 value);

#endif /* HAVE_SGEN_GC */

#endif /* __MONO_SGENGC_H__ */