Roslyn分析大量C#项目未调用Solution.Emit()仍加载程序集致OutOfMemoryException
针对C#大规模代码分析的程序集加载问题解答
1. 触发程序集加载的其他常见操作
除Solution.Emit()外,以下操作均会触发引用程序集加载到内存:
- 元数据解析类API调用:
Compilation.GetTypeByMetadataName、Compilation.GetEntryPoint、ISymbol.GetAttributes()等需要跨程序集解析元数据的方法,会激活MetadataReference.CreateFromFile引入的外部引用的加载逻辑 - MSBuildWorkspace默认加载流程:调用
MSBuildWorkspace.OpenSolutionAsync/OpenProjectAsync加载项目时,内置的依赖解析逻辑会自动将所有关联的程序集以元数据映射形式加载到内存,无需主动调用Emit相关接口 - 跨程序集Symbol访问:当访问当前项目Symbol的
ContainingAssembly、BaseType等属性,且对应定义位于外部引用程序集时,会触发对应程序集的懒加载
2. 无需加载程序集到执行上下文、无需单独AppDomain的实现方案
可以实现,推荐以下两种落地路径:
- 用字节流/字节数组构建元数据引用:使用
MetadataReference.CreateFromImage或CreateFromStream方法,传入程序集的只读字节内容构建引用,Roslyn仅会解析其元数据结构,不会将程序集加载到当前应用域的执行上下文中,也不会产生文件锁定问题。示例代码:
// 仅读取文件字节,不触发程序集加载 var referenceDllBytes = File.ReadAllBytes("your/reference/path.dll"); var metadataReference = MetadataReference.CreateFromImage(referenceDllBytes);
- 用可收集AssemblyLoadContext替代AppDomain(适用于.NET Core 3.0+/.NET 5+):创建独立的可回收
AssemblyLoadContext实例,在该上下文内完成所有Compilation创建、Symbol解析操作,分析完成后调用Unload()方法即可完全释放所有关联的内存和元数据引用,资源开销远低于AppDomain,也无需处理跨域通信问题。
额外优化建议:创建Compilation时指定MetadataReferenceProperties的EmbedInteropTypes = false,关闭互操作程序集自动加载逻辑,减少不必要的资源占用。
内容的提问来源于stack exchange,提问作者TWT
相关产品推荐
相关产品推荐

