gpt4 book ai didi

.net - 没有为泛型类调用 ICorProfilerCallback::ClassUnloadStarted,即使该类已卸载

转载 作者:行者123 更新时间:2023-12-04 12:50:34 24 4
gpt4 key购买 nike

我目前正在调试我公司的 CLR 分析器(通过 ASP.NET 4.7.3282.0、.NET 框架 4.7.2),并看到 CLR 卸载泛型类的场景,但 ClassUnloadStarted不调用回调。

简而言之,我们的分析器根据 ClassID 跟踪加载的类,遵循 ClassLoadStarted、ClassLoadFinished 和 ClassUnloadStarted 回调。在某些时候,类会被卸载(连同其相关模块),但不会为相关 ClassID 调用 ClassUnloadStarted 回调。因此,我们留下了一个停顿的 ClassID,认为该类仍在加载。稍后,当我们尝试查询该 ClassID 时,CLR 不出所料地崩溃了(因为它现在指向垃圾内存)。

我的问题,考虑到下面的详细情况:

  • 为什么我的(通用)类没有调用 ClassUnloadStarted?
  • 这是 CLR 的预期边缘情况行为,还是可能是 CLR/Profiling API 错误?

  • 我找不到任何关于此行为的文档或推理,特别是 ClassUnloadStarted 没有被调用。我在 CoreCLR 代码中也找不到任何提示。提前感谢您的帮助!

    详细场景:

    这是有问题的类(class)( IComparable(T) T=ClassFromModuleFoo ):
    System/IComparable`1<ClassFromModuleFoo>

    在应用程序运行时,问题在某些模块被卸载后出现。
    这是基于添加的调试打印的确切加载/卸载回调流程:
  • 类(class)System/IComparable'1(ClassFromModuleFoo) , mscorlib,已加载。
  • 紧接着类(class)ClassFromModuleFoo ,模块 Foo,被加载到程序集 #1。
  • 模块 Foo 完成加载到程序集 #1。
  • 然后,模块 Foo 再次加载到不同的程序集#2 中。
  • IComparableClassFromModuleFoo再次加载,这次是在程序集 #2 中。现在每个类有两个实例:一个在 Foo 中加载到程序集 #1 中,另一个在 Foo 中加载在程序集 #2 中。
  • 模块 Foo 开始从程序集 #1 中卸载。
  • ClassUnloadStartedClassFromModuleFoo 调用回调在装配#1中。
  • 模块 Foo 已完成从程序集 #1 中卸载。
  • ClassUnloadStarted不是 要求 System/IComparable'1(ClassFromModuleFoo)程序集#1 之后的任何时候(即使它的模块已卸载并且它的 ClassID 指向现在被颠簸的内存)。

  • 一些附加信息:
  • 最新的 .NET 框架版本 4.8 预览版也重现了该问题。
  • 我通过添加 COR_PRF_DISABLE_ALL_NGEN_IMAGES 禁用了原生图像到探查器事件掩码,认为它可能会影响 ClassLoad* 回调,但它没有任何区别。我验证了 mscorlib.dll在确实加载而不是其 native 图像。

  • 编辑:

    感谢我非常聪明的同事,我能够通过一个小示例项目重现该问题,该项目通过加载和卸载 AppDomain 来模拟这种情况。这里是:
    https://github.com/shaharv/dotnet/tree/master/testers/module-load-unload

    测试中的此类发生崩溃,该类已卸载,并且 CLR 未调用卸载回调:
    Loop/MyGenList`1<System/String>

    这是相关代码,加载和卸载了几次:
    namespace Loop
    {
    public class MyGenList<T>
    {
    public List<T> _tList;

    public MyGenList(List<T> tList)
    {
    _tList = tList;
    }
    }

    class MyGenericTest
    {
    public void TestFunc()
    {
    MyGenList<String> genList = new MyGenList<String>(new List<string> { "A", "B", "C" });

    try
    {
    throw new Exception();
    }
    catch (Exception)
    {

    }
    }
    }
    }

    在某些时候,探查器在尝试查询该类的 ClassID 时崩溃 - 认为它仍然有效,因为未调用卸载回调。

    在旁注中,我尝试将此示例移植到 .NET Core 以进行进一步调查,但无法弄清楚如何,因为 .NET Core 不支持辅助 AppDomain(而且我不太确定它是否支持按需组装一般卸货)。

    最佳答案

    在 .Net Core 中实现它之后(3.0 之前不支持卸载),我们设法复制了它(感谢 valiano!)。 coreclr 团队 (https://github.com/dotnet/coreclr/issues/26126) 确认这是一个错误。

    从davmason的解释:

    There are three separate types involved and each callback is only giving you two (but a different set of two).

    Plugin.MyGenList1: the unbound generic type Plugin.MyGenList1 : the generic type bound to thecanonical type (used for normal references) Plugin.MyGenList1 : the generic type bound to System.String. For ClassLoadStarted we have logic that that specifically excludes unbound generic types (i.e. Plugin.MyGenList1) from being shown to the profiler in ClassLoader::Notify

    This means you ClassLoadStarted only gives you callbacks for the canonical and string instances. This seems the right thing to do here, since as a profiler you would only care about bound generic types and there's nothing of interest for unbound ones.

    The issue is that we do a different set of filtering for ClassUnloadStarted. That callback occurs inside EEClass::Destruct, and Destruct is only called on non-generic types, unbound generic types, and canonical generic types. Non-canonical generic types ( i.e. Plugin.MyGenList1 ) are skipped.

    关于.net - 没有为泛型类调用 ICorProfilerCallback::ClassUnloadStarted,即使该类已卸载,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/54883986/

    24 4 0
    Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
    广告合作:1813099741@qq.com 6ren.com