c# memory-leaks marshalling appdomain compileassemblyfromsource

c# - ¿Cómo puedo evitar que CompileAssemblyFromSource pierda memoria?



memory-leaks marshalling (4)

¿Puedes esperar hasta .NET 4.0? Con él puede usar árboles de expresión y el DLR para generar dinámicamente el código sin el problema de pérdida de memoria del código gen.

Otra opción es usar .NET 3.5 con un lenguaje dinámico como IronPython.

EDITAR: Ejemplo de Árbol de Expresión

http://www.infoq.com/articles/expression-compiler

Tengo un código C # que utiliza CSharpCodeProvider.CompileAssemblyFromSource para crear un ensamblaje en la memoria. Una vez que el ensamblaje se ha recolectado en la basura, mi aplicación usa más memoria que antes de crear el ensamblaje. Mi código está en una aplicación web ASP.NET, pero he duplicado este problema en un WinForm. Estoy usando System.GC.GetTotalMemory (true) y Red Gate ANTS Memory Profiler para medir el crecimiento (alrededor de 600 bytes con el código de muestra).

A partir de la búsqueda que he hecho, parece que la filtración proviene de la creación de nuevos tipos, no realmente de ningún objeto al que tengo referencias. Algunas de las páginas web que he encontrado mencionan algo sobre AppDomain, pero no entiendo. ¿Alguien puede explicar qué está pasando aquí y cómo solucionarlo?

Aquí hay algunos ejemplos de código para fugas:

private void leak() { CSharpCodeProvider codeProvider = new CSharpCodeProvider(); CompilerParameters parameters = new CompilerParameters(); parameters.GenerateInMemory = true; parameters.GenerateExecutable = false; parameters.ReferencedAssemblies.Add("system.dll"); string sourceCode = "using System;/r/n"; sourceCode += "public class HelloWord {/r/n"; sourceCode += " public HelloWord() {/r/n"; sourceCode += " Console.WriteLine(/"hello world/");/r/n"; sourceCode += " }/r/n"; sourceCode += "}/r/n"; CompilerResults results = codeProvider.CompileAssemblyFromSource(parameters, sourceCode); Assembly assembly = null; if (!results.Errors.HasErrors) { assembly = results.CompiledAssembly; } }

Actualización 1: Esta pregunta puede estar relacionada: Cargar y descargar dinámicamente un dll generado usando CSharpCodeProvider

Actualización 2: Intentando comprender mejor los dominios de aplicación, encontré esto: ¿Qué es un dominio de aplicación? Una explicación para los principiantes de .Net

Actualización 3: Para aclarar, estoy buscando una solución que proporcione la misma funcionalidad que el código anterior (compilación y acceso al código generado) sin pérdida de memoria. Parece que la solución implicará la creación de un nuevo dominio de aplicación y cálculo de referencias.


Creo que tengo una solución de trabajo. Gracias a todos por apuntarme en la dirección correcta (espero).

Los ensamblajes no se pueden descargar directamente, pero AppDomains sí puede. Creé una biblioteca auxiliar que se carga en un nuevo dominio de aplicación y es capaz de compilar un nuevo conjunto a partir del código. Así es como se ve la clase en esa biblioteca auxiliar:

public class CompilerRunner : MarshalByRefObject { private Assembly assembly = null; public void PrintDomain() { Console.WriteLine("Object is executing in AppDomain /"{0}/"", AppDomain.CurrentDomain.FriendlyName); } public bool Compile(string code) { CSharpCodeProvider codeProvider = new CSharpCodeProvider(); CompilerParameters parameters = new CompilerParameters(); parameters.GenerateInMemory = true; parameters.GenerateExecutable = false; parameters.ReferencedAssemblies.Add("system.dll"); CompilerResults results = codeProvider.CompileAssemblyFromSource(parameters, code); if (!results.Errors.HasErrors) { this.assembly = results.CompiledAssembly; } else { this.assembly = null; } return this.assembly != null; } public object Run(string typeName, string methodName, object[] args) { Type type = this.assembly.GetType(typeName); return type.InvokeMember(methodName, BindingFlags.InvokeMethod, null, assembly, args); } }

Es muy básico, pero fue suficiente para probar. PrintDomain está ahí para verificar que vive en mi nuevo AppDomain. La compilación toma algo de código fuente e intenta crear un ensamblaje. Ejecutar nos permite probar la ejecución de métodos estáticos desde el código fuente dado.

Así es como uso la biblioteca auxiliar:

static void CreateCompileAndRun() { AppDomain domain = AppDomain.CreateDomain("MyDomain"); CompilerRunner cr = (CompilerRunner)domain.CreateInstanceFromAndUnwrap("CompilerRunner.dll", "AppDomainCompiler.CompilerRunner"); cr.Compile("public class Hello { public static string Say() { return /"hello/"; } }"); string result = (string)cr.Run("Hello", "Say", new object[0]); AppDomain.Unload(domain); }

Básicamente, crea el dominio, crea una instancia de mi clase auxiliar (CompilerRunner), lo usa para compilar un nuevo conjunto (oculto), ejecuta algunos códigos de ese nuevo conjunto y luego descarga el dominio para liberar memoria.

Notará el uso de MarshalByRefObject y CreateInstanceFromAndUnwrap. Estos son importantes para garantizar que la biblioteca auxiliar realmente vive en el nuevo dominio.

Si alguien nota algún problema o tiene sugerencias para mejorar esto, me encantaría escucharlo.


La descarga de un conjunto no es compatible. Alguna información sobre por qué se puede encontrar here . Puede encontrar información sobre el uso de un dominio de aplicación here .


También puede encontrar útil esta entrada de blog: Uso de dominio de aplicación para cargar y descargar conjuntos dinámicos. Proporciona un código de ejemplo que muestra cómo crear un dominio de aplicación, cargar un ensamblaje (dinámico) en él, realizar algún trabajo en el nuevo dominio de aplicación y luego descargarlo.

Edición: enlace fijo como se señala en los comentarios a continuación.