首先,我使用Windows 10与visual studio 2019使用.net core 3.1我使用Nuget的Roslyn Microsoft.Net.Compilers 3.4.0和Microsoft.CodeAnalysis 3.4.0。
我在我的项目中使用Roslyn来动态编译脚本。我注意到我的应用程序在编译一些非常小的脚本(5-10kb)时,内存占用率非常高(每个脚本占用20-50MB的内存)。
我最初以为内存的使用是来自于加载程序集,但我将其隔离开来。CSharpCompilation.Create
这似乎需要10MB,而 CSharpCompilation.Emit
把剩下的(10-40MB)。
我减少了它来重现这个bug
public byte[] CompileCSharpCode(string assemblyName, string code)
{
SyntaxTree syntaxTree = SyntaxFactory.ParseSyntaxTree(code, null, "");
CSharpCompilation compilation = CSharpCompilation.Create
(
assemblyName,
new[] { syntaxTree },
options: new CSharpCompilationOptions(OutputKind.DynamicallyLinkedLibrary),
references: _referencesList
);
//at this point the memory usage goes up by 10MB
using (var stream = new MemoryStream())
{
var emitResult = compilation.Emit(stream);
//at this point the memory usage goes up by 10-40MB
return stream.ToArray();
}
}
我希望在调用函数、创建对象和编译时,内存会增加,但内存一直没有释放。在编译了200个脚本后,内存使用量达到了几GB。
即使我把编译时使用的所有对象,包括保存编译结果的字节数组都处理掉,内存也一直居高不下。
目前,我不得不创建第二个.net core应用程序,我调用它将每个脚本1个1个地预编译到.dll文件中,然后加载到内存中(200个脚本总共需要20MB的内存)。
有没有一种方法可以用Roslyn编译我的脚本,而不会出现这种巨大的内存问题?
问题是Roslyn正在为你的编译创建和加载一个汇编,而且它从未被卸载。 使用.NET Core 3.0中新的可收集的AssemblyLoadContext功能,这样你就可以卸载它。 我写了 本文 来证明它。