Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Garbage collection behaviour difference between .NetFramework 4.8 and .Net 5

To detect potential memory-leaks in places where it already happened a lot I have work with tests that are build like the shown below. The main idea is to have an instance, not reference it any longer and let the garbage collector collect it. I would like not to focus on whether this is a good technique or not (in my particular case it did an excellent job) but I would like to focus on the following question:

The code below works perfectly on .NetFramework 4.8 but does not on .Net 5. Why?

[Test]
public void ConceptualMemoryLeakTest()
{
    WeakReference weakReference;
    {
        object myObject = new object();
        weakReference = new WeakReference(myObject);
        myObject = null;
        Assert.Null(myObject);
    }
    Assert.True(weakReference.IsAlive); // instance not collected by GC
    GC.Collect();
    GC.WaitForPendingFinalizers();
    GC.WaitForFullGCComplete();
    GC.Collect();
    Assert.False(weakReference.IsAlive); // instance collected by GC
}

You can see the main idea is to work with a WeakReference and use IsAlive to determine whether the instance got removed by the GC. How did the rules in the new CLR (the one originating from dotnet core) change? I know that what is done here does not relay on something that is specified. Rather I just exploit the behavior I see with the CLR in NetFramework 4.8.

Do you have an idea how to get something similar again that works also with .Net 5?

like image 927
Sjoerd222888 Avatar asked Oct 24 '25 04:10

Sjoerd222888


1 Answers

The reason is likely tiered compilation. In simple words, tiered compilation will (for some methods under some conditions) first compile crude, low optimized version of a method, and then later will prepare a better optimized version if necessary. This is enabled by default in .NET 5 (and .NET Core 3+), but is not available in .NET 4.8.

In your case the result is your method is compiled with mentioned "quick" compilation and is not optimized enough for your code to work as you expect (that is lifetime of myObject variable extends until the end of the method). That is the case even if you compile in Release mode with optimizations enabled, and without any debugger attached.

You can disable tiered compilation by adding:

<TieredCompilation>false</TieredCompilation>

To some <PropertyGroup> inside csproj file for your .NET 5 project, then you'll observe the same behaviour you have in .NET 4.8 case.

Another alternative (besides moving variable to another method and returning WeakReference from it) is to use:

[MethodImpl(MethodImplOptions.AggressiveOptimization)]

attribute on ConceptualMemoryLeakTest method.

like image 110
Evk Avatar answered Oct 25 '25 16:10

Evk



Donate For Us

If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!