val*_*ano 7 .net generics clr profiling clr-profiling-api
我目前正在调试公司的CLR探查器(通过ASP.NET 4.7.3282.0,.NET Framework 4.7.2),并看到CLR卸载通用类但未调用ClassUnloadStarted回调的情况。
简而言之,我们的探查器根据ClassLoadStarted,ClassLoadFinished和ClassUnloadStarted回调跟踪基于ClassID的加载类。在某些时候,该类(及其相关模块)将被卸载,但相关类ID不会调用ClassUnloadStarted回调。因此,我们剩下一个停滞的ClassID,以为该类仍在加载。稍后,当我们尝试查询该ClassID时,CLR意外崩溃(因为它现在指向垃圾内存)。
我的问题,考虑以下详细情况:
我找不到有关此行为的任何文档或理由,特别是ClassUnloadStarted没有被调用。我也没有在CoreCLR代码中找到任何提示。在此先感谢您的帮助!
详细方案:
这是所讨论的类(IComparable(T)带有T=ClassFromModuleFoo):
System/IComparable`1<ClassFromModuleFoo>
Run Code Online (Sandbox Code Playgroud)
当应用程序运行时,在卸载了某些模块后,问题就会显现出来。
这是确切的加载/卸载回调流程,基于添加的调试打印:
System/IComparable'1(ClassFromModuleFoo)mscorlib 的类已加载。ClassFromModuleFoo将模块Foo 的class 加载到程序集#1中。IComparable和ClassFromModuleFoo再次加载,这一次是在组装#2。现在,每个类有两个实例:一个在装配#1中加载的Foo中,一个在装配#2中加载的Foo中。ClassUnloadStartedClassFromModuleFoo在程序集1中需要回调。ClassUnloadStarted以后任何时候都不会调用System/IComparable'1(ClassFromModuleFoo)程序集1(即使已卸载其模块且其ClassID指向现在已损坏的内存)。一些其他信息:
COR_PRF_DISABLE_ALL_NGEN_IMAGES到事件探查器事件掩码来禁用本机映像,认为它可能会影响ClassLoad *回调,但并没有任何区别。我验证mscorlib.dll了确实在加载而不是其本机映像。编辑:
多亏我非常聪明的同事,我才能够通过一个小示例项目来重现该问题,该项目通过加载和卸载AppDomains来模拟这种情况。在这里是:https :
//github.com/shaharv/dotnet/tree/master/testers/module-load-unload
崩溃发生在测试中的此类上,该类已被卸载,并且CLR没有针对该类调用卸载回调:
Loop/MyGenList`1<System/String>
Run Code Online (Sandbox Code Playgroud)
这是相关的代码,它被加载和卸载了几次:
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)
{
}
}
}
}
Run Code Online (Sandbox Code Playgroud)
在某个时候,探查器在尝试查询该类的ClassID时崩溃-认为它仍然有效,因为未为其调用回调。
附带说明一下,我尝试将此示例移植到.NET Core进行进一步的研究,但无法弄清楚怎么做,因为.NET Core不支持辅助AppDomain(而且我不太确定它是否支持按需组装)卸载)。
在 .Net Core 中实现这一功能后(3.0 之前不支持卸载),我们成功地复制了它(感谢 valiano!)。coreclr 团队确认这是一个错误(https://github.com/dotnet/coreclr/issues/26126)。
根据达马森的解释:
涉及三种不同的类型,每个回调只给您两个(但两个不同的集合)。
Plugin.MyGenList1:未绑定的泛型类型 Plugin.MyGenList1:绑定到规范类型的泛型类型(用于普通引用) Plugin.MyGenList1:绑定到 System.String 的泛型类型。对于 ClassLoadStarted,我们有专门排除未绑定泛型类型(即 Plugin.MyGenList1)向 ClassLoader::Notify 中的探查器显示的逻辑
这意味着 ClassLoadStarted 只为您提供规范实例和字符串实例的回调。这似乎是正确的做法,因为作为探查器,您只关心绑定的泛型类型,而对未绑定的类型没有任何兴趣。
问题是我们对 ClassUnloadStarted 进行了一组不同的过滤。该回调发生在 EEClass::Destruct 内部,并且 Destruct 仅在非泛型类型、未绑定泛型类型和规范泛型类型上调用。跳过非规范泛型类型(即 Plugin.MyGenList1 )。
| 归档时间: |
|
| 查看次数: |
300 次 |
| 最近记录: |