سؤال

لدي بعض الكود C# الذي يستخدم csharpCodeProvider.compileassemblyFromsource لإنشاء مجموعة في الذاكرة. بعد جمع التجميع ، يستخدم طلبي ذاكرة أكثر مما كان عليه قبل إنشاء التجميع. الكود الخاص بي موجود في تطبيق الويب ASP.NET ، لكنني قمت بتكرار هذه المشكلة في Winform. أنا أستخدم system.gc.gettotalmemory (True) و Red Gate Ants Memory Profiler لقياس النمو (حوالي 600 بايت مع رمز العينة).

من البحث الذي قمت به ، يبدو أن التسرب يأتي من إنشاء أنواع جديدة ، وليس حقًا من أي كائنات أحملها. ذكرت بعض صفحات الويب التي وجدتها شيئًا عن AppDomain ، لكنني لا أفهم. هل يمكن لأحد أن يشرح ما يجري هنا وكيفية إصلاحه؟

إليك بعض كود عينة للتسرب:

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;
    }
}

تحديث 1: قد يكون هذا السؤال مرتبطًا: تحميل وتفريغ AA DLL بشكل ديناميكي باستخدام CSHARPCODEPROVIDER

تحديث 2: محاولة فهم مجالات التطبيق أكثر ، وجدت هذا: ما هو مجال التطبيق - شرح للمبتدئين .NET

تحديث 3: لتوضيح ، أنا أبحث عن حل يوفر نفس الوظيفة مثل الكود أعلاه (تجميع وتوفير الوصول إلى التعليمات البرمجية التي تم إنشاؤها) دون تسرب الذاكرة. يبدو أن الحل سيشمل إنشاء AppDomain و NEWSTER.

هل كانت مفيدة؟

المحلول

تفريغ التجميع غير مدعوم. بعض المعلومات حول لماذا يمكن العثور عليها هنا. يمكن العثور على بعض المعلومات حول استخدام AppDomain هنا.

نصائح أخرى

أعتقد أن لدي حل عمل. شكرا للجميع على توجيههم لي في الاتجاه الصحيح (آمل).

لا يمكن تفريغ التجميعات مباشرة ، ولكن يمكن أن. لقد قمت بإنشاء مكتبة مساعدة يتم تحميلها في AppDomain جديد وقادر على تجميع مجموعة جديدة من الكود. إليك ما يبدو عليه الفصل في مكتبة المساعدة:

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);
    }

}

إنه أساسي للغاية ، لكنه كان يكفي للاختبار. PrintDomain موجود للتحقق من أنه يعيش في appdomain الجديد. يأخذ Compile بعض التعليمات البرمجية المصدر ويحاول إنشاء مجموعة. يتيح لنا تشغيل اختبار تنفيذ طرق ثابتة من رمز المصدر المحدد.

إليك كيفية استخدام مكتبة المساعد:

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);
}

إنه ينشئ المجال بشكل أساسي ، ويقوم بإنشاء مثيل لفئة المساعد الخاصة بي (compilerrunner) ، ويستخدمه لتجميع مجموعة جديدة (مخفية) ، ويدير بعض التعليمات البرمجية من تلك التجميع الجديد ، ثم يفرغ المجال لتحرير الذاكرة.

ستلاحظ استخدام MarshalByRefObject و CreateInstanceFromandunWrap. هذه مهمة لضمان أن مكتبة المساعد تعيش حقًا في المجال الجديد.

إذا لاحظ أي شخص أي مشاكل أو لديه اقتراحات لتحسين هذا ، أحب أن أسمعها.

يمكنك أيضًا العثور على إدخال المدونة هذا مفيد: باستخدام AppDomain لتحميل وتفريغ التجميعات الديناميكية. يوفر بعضًا من التعليمات البرمجية التي توضح كيفية إنشاء AppDomain ، وتحميل مجموعة (ديناميكية) فيه ، قم ببعض الأعمال في AppDomain الجديدة ثم تفريغها.

تحرير: رابط ثابت كما هو موضح في التعليقات أدناه.

هل يمكنك الانتظار حتى .NET 4.0؟ مع ذلك ، يمكنك استخدام أشجار التعبير و DLR لإنشاء رمز ديناميكيًا بدون مشكلة فقدان الذاكرة ذات الكود.

خيار آخر هو استخدام .NET 3.5 مع لغة ديناميكية مثل IronPython.

تحرير: مثال شجرة التعبير

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

مرخصة بموجب: CC-BY-SA مع الإسناد
لا تنتمي إلى StackOverflow
scroll top