为什么 .NET 5 GC 不收集或至少调用 Finalize明确取消引用的对象?

如何解决为什么 .NET 5 GC 不收集或至少调用 Finalize明确取消引用的对象?

我想测试垃圾收集器,但这样做很困难。

我写了以下琐碎的测试代码

using System;

class Foo
{
   int  i;
        
   public Foo(int v)
   {
      i = v;
      Console.WriteLine($"{i} was born");
   }
   ~Foo()
   {
       Console.WriteLine($"{i} has died");
   }
}

public class Program
{
   [STAThread]
   public static void Main(string[] args)
   {
       Foo n1 = new Foo(1);
       Foo n2 = new Foo(2);
       Foo n3 = new Foo(3);
       Foo n4 = new Foo(4);
            
       Console.WriteLine("built everything");
       GC.Collect(GC.MaxGeneration,GCCollectionMode.Forced,true,true);
       GC.WaitForPendingFinalizers();
       System.Threading.Thread.Sleep(1000);
                        
       n1 = null;
       n2 = null;
       n3 = n4;
            
       Console.WriteLine("deref n1..n3");
       GC.Collect(GC.MaxGeneration,true);
       GC.WaitForPendingFinalizers();
       System.Threading.Thread.Sleep(1000);
       Console.WriteLine("done.");
   }
}

并注意到在 .NET475 和 .NET5 Fiddle 下,

1 出生
2 出生
3出生
4 出生
建造一切
deref n1..n3
完成。

垃圾收集器要么不调用我的终结器,要么不完全收集我取消引用的对象。
(注意:我的机器也是如此,没有在线小提琴)

有趣的是,当尝试 Roslyn3.8 Fiddle 时它似乎有效。

1 出生
2 出生
3出生
4 出生
建造一切
deref n1..n3
3人死亡
2人死亡
1人死亡
完成。

(注意:它甚至可以在没有我所有的“强制一切”到 GC 的情况下工作。)

我目前对 .NET GC 有点不信任危机 :)
为什么 .NET 编译器缺少我的终结器?它甚至收集了我的东西吗?

解决方法

我同意这些评论,在尝试从此类示例中理解 GC 时,您应该非常小心。你可以,只要小心点,你真的需要了解你在做什么。

这里是 JIT Tier0 (QuickJIT) 的 Debug 模式或 .NET Core 3.1/.NET 5 默认行为 - 对象的两个范围都被延长到方法结束(或者换句话说,运行时不在乎使它尽可能短)。用 Tiered compilation disabled 运行它,你会看到。

或者向 Main 方法添加优化属性(转换为禁用方法的 Tier0):

[MethodImpl(MethodImplOptions.AggressiveOptimization)]
public static void Main(string[] args)
{
   ...

您可以在 JIT 生成的代码及其对应的 GCInfo 级别(IL 级别不够)观察此行为。

未优化版本(Tier0) - 如您所见,分配结果(rax 调用后的JIT_New)存储在堆栈 (rbp-8rbp-10rbp-18rbp-20)。并且这些堆栈位置报告为 Untracked,在 GCInfo 命名法中表示 “在整个方法生命周期内被视为可访问”

!U /d -gcinfo 00007ffd4a605ec0
Normal JIT generated code
Finalizers.Program.Main(System.String[])
ilAddr is 0000024B68962050 pImport is 0000020B3E9FF5C0
Begin 00007FFD4A605EC0,size 142

...
Code size: 142
Untracked: +rbp+10 +rbp-8 +rbp-10 +rbp-18 +rbp-20
...
.\Program.cs @ 12:
mov     rcx,7FFD4A6D4D90h (MT: Finalizers.Foo)
call    coreclr!JIT_New (00007ffd`aa0806f0)
mov     qword ptr [rbp-8],rax
...
.\Program.cs @ 13:
mov rcx,7FFD4A6D4D90h (MT: Finalizers.Foo)
call    coreclr!JIT_New (00007ffd`aa0806f0)
mov     qword ptr [rbp-10h],rax
...
.\Program.cs @ 14:
mov rcx,7FFD4A6D4D90h (MT: Finalizers.Foo)
call    coreclr!JIT_New (00007ffd`aa0806f0)
mov     qword ptr [rbp-18h],rax
...
.\Program.cs @ 15:
mov rcx,7FFD4A6D4D90h (MT: Finalizers.Foo)
call    coreclr!JIT_New (00007ffd`aa0806f0)
mov     qword ptr [rbp-20h],rax
...

优化(第 1 层) - 如您所见,分配结果确实被丢弃了(rax 被调用覆盖)。此外,正如您所看到的,safepoint(GC 可能挂起线程的地方)没有报告任何根。他们会,如果 GCInfo 想说在给定的安全点在寄存器中有一个根。

0:007> !U /d -gcinfo 00007ffd4a5e5f20
Normal JIT generated code
Finalizers.Program.Main(System.String[])
ilAddr is 00000151A7672050 pImport is 000001C2D08FF1A0
Begin 00007FFD4A5E5F20,size 1cc

...
.\Program.cs @ 12:
0000003a is a safepoint: 
mov     rcx,7FFD4A6B4240h (MT: Finalizers.Foo)
call    coreclr!JIT_New (00007ffd`aa0806f0)
00000049 is a safepoint: 
mov     rcx,rax
mov     edx,1
call    00007ffd`4a5e5b00 (Finalizers.Foo..ctor(Int32),mdToken: 0000000006000003)
.\Program.cs @ 13:
00000056 is a safepoint: 
mov rcx,7FFD4A6B4240h (MT: Finalizers.Foo)
call    coreclr!JIT_New (00007ffd`aa0806f0)
00000065 is a safepoint: 
mov     rcx,2
call    00007ffd`4a5e5b00 (Finalizers.Foo..ctor(Int32),mdToken: 0000000006000003)

.\Program.cs @ 14:
00000072 is a safepoint: 
mov rcx,7FFD4A6B4240h (MT: Finalizers.Foo)
call    coreclr!JIT_New (00007ffd`aa0806f0)
00000081 is a safepoint: 
mov     rcx,3
call    00007ffd`4a5e5b00 (Finalizers.Foo..ctor(Int32),mdToken: 0000000006000003)

.\Program.cs @ 15:
0000008e is a safepoint: 
mov rcx,7FFD4A6B4240h (MT: Finalizers.Foo)
call    coreclr!JIT_New (00007ffd`aa0806f0)
0000009d is a safepoint: 
mov     rcx,4
call    00007ffd`4a5e5b00 (Finalizers.Foo..ctor(Int32),mdToken: 0000000006000003)
...

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

相关推荐


Selenium Web驱动程序和Java。元素在(x,y)点处不可单击。其他元素将获得点击?
Python-如何使用点“。” 访问字典成员?
Java 字符串是不可变的。到底是什么意思?
Java中的“ final”关键字如何工作?(我仍然可以修改对象。)
“loop:”在Java代码中。这是什么,为什么要编译?
java.lang.ClassNotFoundException:sun.jdbc.odbc.JdbcOdbcDriver发生异常。为什么?
这是用Java进行XML解析的最佳库。
Java的PriorityQueue的内置迭代器不会以任何特定顺序遍历数据结构。为什么?
如何在Java中聆听按键时移动图像。
Java“Program to an interface”。这是什么意思?
Java在半透明框架/面板/组件上重新绘画。
Java“ Class.forName()”和“ Class.forName()。newInstance()”之间有什么区别?
在此环境中不提供编译器。也许是在JRE而不是JDK上运行?
Java用相同的方法在一个类中实现两个接口。哪种接口方法被覆盖?
Java 什么是Runtime.getRuntime()。totalMemory()和freeMemory()?
java.library.path中的java.lang.UnsatisfiedLinkError否*****。dll
JavaFX“位置是必需的。” 即使在同一包装中
Java 导入两个具有相同名称的类。怎么处理?
Java 是否应该在HttpServletResponse.getOutputStream()/。getWriter()上调用.close()?
Java RegEx元字符(。)和普通点?