微信公众号搜"智元新知"关注
微信扫一扫可直接关注哦!

.Net Framework和.Net Core之间的WeakReference行为不同

如何解决.Net Framework和.Net Core之间的WeakReference行为不同

考虑以下代码

using System;
using System.Collections.Generic;
using System.Runtime.CompilerServices;

#nullable enable

namespace ConsoleApp1
{
    class Program
    {
        static void Main()
        {
            var list    = makeList();
            var weakRef = new WeakReference(list[0]);

            list[0] = null;
            GC.Collect();
            GC.WaitForPendingFinalizers();

            Console.WriteLine(weakRef.IsAlive);
        }

        [MethodImpl(MethodImplOptions.NoInlining)]
        static List<int[]?> makeList()
        {
            return new List<int[]?> { new int[2] };
        }
    }
}
  • 使用.Net Framework 4.8上的发行版或调试版本, 代码显示False
  • 使用.Net上的发行版或调试版 核心3.1,该代码显示True

是什么导致这种行为差异? (这导致我们的某些单元测试失败。)

注意:我将列表初始化放入makeList()中,并关闭了内联,以尝试使.Net Core版本与.Net Framework版本相同,但无济于事。


[EDIT]正如汉斯指出的那样,添加一个循环即可解决该问题。

以下代码 将打印False

var list    = makeList();
var weakRef = new WeakReference(list[0]);

list[0] = null;

for (int i = 0; i < 1; ++i)
    GC.Collect();

Console.WriteLine(weakRef.IsAlive);

但这将打印True

var list    = makeList();
var weakRef = new WeakReference(list[0]);

list[0] = null;

GC.Collect();
GC.Collect();
GC.Collect();
GC.Collect();

// Doesn't seem to matter how many GC.Collect() calls you do.

Console.WriteLine(weakRef.IsAlive);

got 是某种怪异的Jitter事情...

解决方法

仅因为允许收集 并不意味着它已被义务尽快收集。虽然该语言指出允许GC确定不再读取局部变量,因此不认为它是根,但这并不意味着您可以依赖收集局部变量的内容。在您上次阅读后立即显示。

这不是运行时定义的行为之间的某些更改,这是两个运行时中的未定义行为,因此它们之间的差异是完全可以接受的。

,

当我删除列表变量时,我获得了要释放的引用:

using NUnit.Framework;
using System;
using System.Collections.Generic;

namespace NUnitTestProject1
{
    public class Tests
    {
        [TestCase(2,GCCollectionMode.Forced,true)]
        public void TestWeakReferenceWithList(int generation,GCCollectionMode forced,bool blocking)
        {
            static WeakReference CreateWeakReference()
            {
                return new WeakReference(new List<int[]> { new int[2] });
            }

            var x = CreateWeakReference();

            Assert.IsTrue(x.IsAlive);

            GC.Collect(generation,forced,blocking);

            Assert.IsFalse(x.IsAlive);
        }
   }
}

以下测试用例失败:

using NUnit.Framework;
using System;
using System.Collections.Generic;

namespace NUnitTestProject1
{
    public class Tests
    {
        [TestCase(2,bool blocking)
        {
            static List<int[]> CreateList()
            {
                return new List<int[]> { new int[2] };
            }

            WeakReference x;

            {
                var list = CreateList();

                x = new WeakReference(list);

                list = null;
            }
            
            Assert.IsTrue(x.IsAlive);

            GC.Collect(generation,blocking);

            Assert.IsFalse(x.IsAlive);
        }
   }
}

如果查看IL,我们可以确定将null分配给局部变量1:

IL_0003:  call       class [System.Collections]System.Collections.Generic.List`1<int32[]> NUnitTestProject1.Tests::'<TestWeakReferenceWithList>g__CreateList|0_0'()
IL_0008:  stloc.1
IL_0009:  ldloc.1
IL_000a:  newobj     instance void [System.Runtime]System.WeakReference::.ctor(object)
IL_000f:  stloc.0
IL_0010:  ldnull
IL_0011:  stloc.1
IL_0012:  nop

版权声明:本文内容由互联网用户自发贡献,该文观点与技术仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 dio@foxmail.com 举报,一经查实,本站将立刻删除。