C# DebuggerAttribute在Debug/Release下GC 对象回收的影响

发布时间 2023-09-05 19:56:19作者: mikodopants

一、DebuggerAttribute

DebuggerAttribute为作用在程序集上的特性,按照微软文档对该特性的描述,该特性用于修改运行时实时 (JIT) 调试的代码生成。而在Debug下编译并生成的程序集,JIT会将变量保存至方法结束。这将影响以下代码的一些表现。

1、Debug

程序集在Debug模式下默认的DebuggerAttribute如下,通过ILspy可以看到。

// D:\vs2019 proj\EventTest\EventTest\bin\Debug\net6.0\EventTest.dll
// EventTest, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null
// Global type: <Module>
// Entry point: EventTest.Program.Main
// Architecture: AnyCPU (64-bit preferred)
// Runtime: v4.0.30319
// This assembly was compiled using the /deterministic option.
// Hash algorithm: SHA1
// Debug info: Loaded from portable PDB: D:\vs2019 proj\EventTest\EventTest\bin\Debug\net6.0\EventTest.pdb

using System.Diagnostics;
using System.Reflection;
using System.Runtime.CompilerServices;
using System.Runtime.Versioning;

[assembly: CompilationRelaxations(8)]
[assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)]
[assembly: Debuggable(DebuggableAttribute.DebuggingModes.Default | DebuggableAttribute.DebuggingModes.DisableOptimizations | DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints | DebuggableAttribute.DebuggingModes.EnableEditAndContinue)]
[assembly: TargetFramework(".NETCoreApp,Version=v6.0", FrameworkDisplayName = ".NET 6.0")]
[assembly: AssemblyCompany("EventTest")]
[assembly: AssemblyConfiguration("Debug")]
[assembly: AssemblyFileVersion("1.0.0.0")]
[assembly: AssemblyInformationalVersion("1.0.0")]
[assembly: AssemblyProduct("EventTest")]
[assembly: AssemblyTitle("EventTest")]
[assembly: AssemblyVersion("1.0.0.0")]
ILspy on assembly

控制台应用程序的Main函数是这样的。

    private class TestClass
    {
        public void Test()
        {
            Subscriber sub = new Subscriber();
            sub = null;
            GC.Collect();
        }
    }

    private static void Main(string[] args)
    {
        TestClass testClass = new TestClass();
        testClass.Test();
        Console.WriteLine("Test方法结束");
        Console.ReadLine();
        Debugger.Break();
    }
public class Subscriber
{
    public Subscriber()
    {
        Console.WriteLine("Subscriber created");
    }

    public void Subscribe(object o, EventArgs e)
    {
        Console.WriteLine("Subscriber " + DateTime.Now);
    }

    ~Subscriber()
    {
        Console.WriteLine("Subscriber Finalize");
    }
}
Subscriber

 

//Output
Subscriber created
Test方法结束

运行后将不会看到Subscriber的终结器被执行。

2、Release

将以上代码以Release模式下编译后运行。在程序结束前执行了Subscriber的终结器。

//Output
Subscriber created
Test方法结束
Subscriber Finalize

Release模式下的DebuggerAttribute。

using System.Diagnostics;
using System.Reflection;
using System.Runtime.CompilerServices;
using System.Runtime.Versioning;

[assembly: CompilationRelaxations(8)]
[assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)]
[assembly: Debuggable(DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints)]
[assembly: TargetFramework(".NETCoreApp,Version=v6.0", FrameworkDisplayName = ".NET 6.0")]
[assembly: AssemblyCompany("EventTest")]
[assembly: AssemblyConfiguration("Release")]
[assembly: AssemblyFileVersion("1.0.0.0")]
[assembly: AssemblyInformationalVersion("1.0.0")]
[assembly: AssemblyProduct("EventTest")]
[assembly: AssemblyTitle("EventTest")]
[assembly: AssemblyVersion("1.0.0.0")]
ILspy on assembly with release mode

二、在Debug下回收对象

1、在Test()方法外回收

前文提过,在Debug下编译并生成的程序集,JIT会将变量保存至方法结束,所以TestClass.Test()方法中的GC.collect()没有把对象回收掉。那么在Test()方法结束后再执行一次回收即可把GC.Collect()即可把对象回收掉。
注:其实不管是Debug还是在Release下,TestClass.Test()方法中的Subscriber对象在线程离开Test()方法后会被变为无根对象,等待下一次GC的回收。

2、在Test()方法内回收

既然是通过DebuggerAttribute影响JIT对变象的保存,那么在Debug模式下指定DebuggerAttribute即可改变JIT的前述的行为。

//MyAssemblyInfo.cs
using System.Diagnostics;

[assembly: Debuggable(DebuggableAttribute.DebuggingModes.Default)]
MyAssemblyInfo.cs

在项目中新建文件,内容如上。(注:如果是Net framework项目,可以在项目自带的AssemblyInfo加入以上内容)

将DebuggingModes指定为Default即可让Subscriber对象在Test内回收。终结器的执行顺序在Test方法之后是因为执行终结器的时机是由GC决定,并不由程序员决定。

//Output
Subscriber created
Test方法结束
Subscriber Finalize

 

 

更多信息查看以下链接:

终结器 - C# 编程指南 - C# | Microsoft Learn

GC.Collect 方法 (System) | Microsoft Learn

DebuggableAttribute 类 (System.Diagnostics) | Microsoft Learn

DebuggableAttribute.DebuggingModes 枚举 (System.Diagnostics) | Microsoft Learn

C # 编译器选项 - 代码生成选项 - C# | Microsoft Learn