You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
{{ message }}
Repository navigation
[mono][gc] Objects larger than LOS section size call the LOS heap to grow without bound #97991
longbigger_than_los_section=32*1024*1024;longbigger_than_small_obj=100*1024;longsmall_obj=50;longalloc_size=bigger_than_small_obj;longcnt=0;long[]data=newlong[1024*1024*32];varend=DateTime.Now+TimeSpan.FromMilliseconds(10000);do{// these are just dummy computations to keep CPU busyInterlocked.Increment(refcnt);data=newlong[alloc_size];data[0]=cnt;// force some GCif(cnt%10000==0){GC.Collect();vartotal=GC.GetTotalMemory(true);Console.WriteLine("GC.GetTotalMemory: "+total);}}while(DateTime.Now<end);
if alloc_size is small_obj then the LOS size stays constant, as we would expect
$ MONO_LOG_MASK=gc MONO_LOG_LEVEL=debug dotnet run...Mono: GC_MAJOR: (user request) time 0.05ms, stw 0.08ms los size: 1024K in use: 24K...Mono: GC_MAJOR: (user request) time 0.05ms, stw 0.06ms los size: 1024K in use: 24K
if alloc_size is bigger_than_small_obj then we also have a LOS size that stays constant
$ MONO_LOG_MASK=gc MONO_LOG_LEVEL=debug dotnet run...Mono: GC_MAJOR_CONCURRENT_FINISH: (finishing) time 0.23ms, stw 0.12ms los size: 3072K in use: 1625K...Mono: GC_MAJOR_CONCURRENT_FINISH: (finishing) time 0.24ms, stw 0.14ms los size: 3072K in use: 1625K
if alloc_size is bigger_than_los_section then the LOS heap size grows bigger and bigger (despite us not actually placing the new long[alloc_size] in the LOS section (!)
$ MONO_LOG_MASK=gc MONO_LOG_LEVEL=debug dotnet run...Mono: GC_MAJOR_CONCURRENT_FINISH: (finishing) time 0.20ms, stw 0.13ms los size: 525344K in use: 524313K...Mono: GC_MAJOR_CONCURRENT_FINISH: (finishing) time 0.19ms, stw 0.13ms los size: 787504K in use: 786457K...Mono: GC_MAJOR_CONCURRENT_FINISH: (finishing) time 0.14ms, stw 0.09ms los size: 1049664K in use: 1048601K...Mono: GC_MAJOR_CONCURRENT_FINISH: (finishing) time 0.20ms, stw 0.14ms los size: 1573984K in use: 1572889K...
It's weird that we keep printing a larger and larger LOS size. sgen_los_free_object has a sgen_los_memory_usage_total -= size; in the case where size > LOS_SECTION_OBJECT_LIMIT
I thought maybe the problem is in the call to ensure_free_space before the size > LOS_SECTION_OBJECT_LIMIT check in this code
Update With the interpreter too, it stays constant, like with the JIT. with MONO_ENV_OPTIONS=--interp the LOS size to a higher point and then oscillates there:
$ MONO_ENV_OPTIONS=--interp MONO_LOG_MASK=gc MONO_LOG_LEVEL=debug dotnet run...Mono: GC_MAJOR_CONCURRENT_FINISH: (finishing) time 0.21ms, stw 0.07ms los size: 525344K in use: 524313K...Mono: GC_MAJOR_CONCURRENT_FINISH: (finishing) time 0.23ms, stw 0.16ms los size: 787504K in use: 786457K...Mono: GC_MAJOR_CONCURRENT_FINISH: (finishing) time 0.23ms, stw 0.17ms los size: 2098304K in use: 2097177K...Mono: GC_MAJOR_CONCURRENT_FINISH: (finishing) time 0.23ms, stw 0.16ms los size: 2360464K in use: 2359321K...Mono: GC_MAJOR_CONCURRENT_FINISH: (finishing) time 0.24ms, stw 0.18ms los size: 2360464K in use: 2359321K...Mono: GC_MAJOR_CONCURRENT_FINISH: (finishing) time 0.22ms, stw 0.16ms los size: 2098304K in use: 2097177K...Mono: GC_MAJOR_CONCURRENT_FINISH: (finishing) time 0.24ms, stw 0.17ms los size: 1836144K in use: 1835033K...Mono: GC_MAJOR_CONCURRENT_FINISH: (finishing) time 0.25ms, stw 0.18ms los size: 2360464K in use: 2359321K...Mono: GC_MAJOR_CONCURRENT_FINISH: (finishing) time 0.24ms, stw 0.17ms los size: 2360464K in use: 2359321K...Mono: GC_MAJOR_CONCURRENT_FINISH: (finishing) time 0.23ms, stw 0.16ms los size: 2098304K in use: 2097177K
With the interpreter it climbs higher and then oscillates around 2098304K.
This is on osx-arm64 with the .NET 8.0.1 SDK
I think it's a problem with the test case. This variant runs with constant LOS size:
usingSystem.Runtime.CompilerServices;longbigger_than_los_section=32*1024*1024;longbigger_than_small_obj=100*1024;longsmall_obj=50;longalloc_size=bigger_than_los_section;longcnt=0;long[]data=null;AssignData(refdata,alloc_size);varend=DateTime.Now+TimeSpan.FromMilliseconds(10000);do{// these are just dummy computations to keep CPU busyInterlocked.Increment(refcnt);AssignData(refdata,alloc_size);data[0]=cnt;// force some GCif(cnt%10000==0){GC.Collect();// GC.WaitForPendingFinalizers();// GC.Collect();// GC.WaitForPendingFinalizers();vartotal=GC.GetTotalMemory(true);Console.WriteLine("GC.GetTotalMemory: "+total);}}while(DateTime.Now<end);[MethodImpl(MethodImplOptions.NoInlining)]staticvoidAssignData(reflong[]d,longsize,intrecurse=20){if(recurse>=0){AssignData(refd,size,recurse-1);}else{d=newlong[size];}}
...Mono: GC_MAJOR_CONCURRENT_FINISH: (finishing) time 0.22ms, stw 0.14ms los size: 525344K in use: 524313K...Mono: GC_MAJOR_CONCURRENT_FINISH: (finishing) time 0.14ms, stw 0.08ms los size: 1049664K in use: 1048601K...Mono: GC_MAJOR_CONCURRENT_FINISH: (finishing) time 0.23ms, stw 0.16ms los size: 787504K in use: 786457K...Mono: GC_MAJOR_CONCURRENT_FINISH: (finishing) time 0.23ms, stw 0.17ms los size: 787504K in use: 786457K...Mono: GC_MAJOR_CONCURRENT_FINISH: (finishing) time 0.23ms, stw 0.17ms los size: 787504K in use: 786457K
it starts a bit lower, spikes and then reaches a steady state of 787504K
I can get the interpreter to hold steady at the same LOS size as the JIT if I chance the recurse dept to be 200 instead of 20
Mono: GC_MAJOR_CONCURRENT_FINISH: (finishing) time 0.22ms, stw 0.13ms los size: 787504K in use: 786457K
...
Mono: GC_MAJOR_CONCURRENT_FINISH: (finishing) time 0.20ms, stw 0.12ms los size: 787504K in use: 786457K
Activity
lambdageek commentedon Feb 5, 2024
It's weird that we keep printing a larger and larger LOS size.
sgen_los_free_objecthas asgen_los_memory_usage_total -= size;in the case wheresize > LOS_SECTION_OBJECT_LIMITI thought maybe the problem is in the call to
ensure_free_spacebefore thesize > LOS_SECTION_OBJECT_LIMITcheck in this coderuntime/src/mono/mono/sgen/sgen-los.c
Lines 442 to 460 in b94a82d
but I don't see
ensure_free_spaceactually asking the memory governor for more space. So it's something insgen-lositself, I guess.lambdageek commentedon Feb 6, 2024
Update With the interpreter too, it stays constant, like with the JIT. with
MONO_ENV_OPTIONS=--interpthe LOS size to a higher point and then oscillates there:With the interpreter it climbs higher and then oscillates around
2098304K.This is on osx-arm64 with the .NET 8.0.1 SDK
I think it's a problem with the test case. This variant runs with constant LOS size:
it starts a bit lower, spikes and then reaches a steady state of 787504K
@pavelsavara @BrzVlad
lambdageek commentedon Feb 6, 2024
I can get the interpreter to hold steady at the same LOS size as the JIT if I chance the
recursedept to be200instead of 20