29

Bellekteki bir derleme oluşturmak için CSharpCodeProvider.CompileAssemblyFromSource kullanan bazı C# kodlarım var. Toplama çöp toplandıktan sonra, başvurum derlemeyi oluşturmadan önce olduğundan daha fazla bellek kullanıyor. Kodum bir ASP.NET web uygulamasında, ancak bu sorunu bir WinForm'da kopyaladım. Büyümeyi ölçmek için System.GC.GetTotalMemory (true) ve Red Gate ANTS Bellek Profilcisi kullanıyorum (örnek kodla yaklaşık 600 bayt).CompileAssemblyFromSource belleğini sızmasını nasıl önleyebilirim?

Yaptığım aramadan, sızıntıların yeni tiplerin oluşturulmasından kaynaklandığını, gerçekten de referansları tuttuğum herhangi bir nesneden kaynaklanmadığını duydum. Bulduğum web sayfalarından bazıları, AppDomain hakkında bir şeyden bahsetti, ama ben anlamıyorum. Birisi burada neler olduğunu ve nasıl düzeltileceğini açıklayabilir mi? Bu soru ile ilgili olabilir: 1

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

Güncelleme: 2 Dynamically loading and unloading a a dll generated using CSharpCodeProvider

Güncelleme: Burada

sızıntı için bazı örnek kod daha uygulama alanlarını anlamaya çalışırken, bunu buldum: What is an application domain - an explanation for .Net beginners

Güncelleme 3: Netleştirmek için, ben looki'yim Yukarıdaki kodla aynı işlevi (çözümlenen kodlara derleme ve erişim sağlama) sağlayan bir çözüm için bellek sızıntısı olmadan. Çözüm, yeni bir AppDomain ve marshaling oluşturmayı içerecek gibi görünüyor.

+0

Çok serin bir soru. Bunu, bugünün sonuna kadar başka bir AppDomain kullanarak nasıl yapacağımıza dair bir örnek vereceğim (şu anda öğle yemeği yiyorum, sonra işe geri dönüyorum ...). – Charles

+0

Ortaya çıkan montaj ile ne yapmayı düşünüyorsunuz? Sadece bir defaya mahsus olmak mı yoksa üzerinde durmak mı? – madaboutcode

+1

@LightX Bir süreliğine ona dayanacağım ve gerektiğinde üyelerini çağırıyorum, ancak kaynak kodun yeni bir sürümü mevcut olduğunda, onu dökmek ve yeni bir derleme oluşturmak istiyorum. yeni kod. AppDomain düzeltmesi olmadan, tekrar eden derlemeler oluşturmaya yönelik bu döngü (eski sürümlere başvurmama rağmen), bellek kullanımının artmasına neden olur. – Nogwater

cevap

13

Montaj boşaltma desteklenmiyor. Neden hakkında bazı bilgiler here bulunabilir. Bir AppDomain kullanımı hakkında bazı bilgiler here bulunabilir.

+4

Jeremy'nin doğru olduğu tüm etki alanını boşaltmanız gerekir, .NET'in bir derlemeyi kaldırması için zorlamanın bir yolu yoktur. Bir appdomain kullanmanız gerekecek (ki bu biraz sinir bozucu, ama gerçekten de o kadar da kötü değil), işiniz bittiğinde her şeyi boşaltabilirsiniz. –

+0

Öyleyse, duyduğum şey, kendi AppDomain'ına yükleyip daha sonra tüm şeyi boşaltmadıkça, bir montajı kaldıramazsınız. Bu uygulanabilir bir çözüm gibi geliyor, bu yüzden dinamik kod derlemek ve kullanmak için bunu nasıl yapabilirim? – Nogwater

7

Bu blog girdisini faydalı bulabilirsiniz: Using AppDomain to Load and Unload Dynamic Assemblies. Bir AppDomain oluşturmanın nasıl olduğunu gösteren bir örnek kod, bir (dinamik) derleme yükler, yeni AppDomain'de bir şeyler yapın ve sonra boşaltın.

Düzenleme: Sabit bağlantı, aşağıdaki yorumlarda belirtildiği gibi.

+0

Bu, bilmem gereken yön gibi görünüyor. Bunu CompileAssemblyFromSource ile kullanabilir miyim? Bir web uygulamasından yeni AppDomains oluşturabilir miyim? – Nogwater

+3

CompileAssemblyFromSource öğesini ayrı bir AppDomain'de çağırıp çağırabilir ve işiniz bittiğinde bu alanı kaldırabilirsiniz. Yukarıdaki –

+1

linki: http://blogs.claritycon.com/steveholstad/2007/06/28/using-appdomain-to-load-and-unload-dynamic-assemblies/ –

30

Bence bir çalışma çözümüm var. Beni doğru yöne işaret eden herkese teşekkürler (umarım).

Yapı grupları doğrudan alınamaz, ancak AppDomains olabilir. Yeni bir AppDoma alanına yüklenen ve yeni bir derlemeyi koddan derleyebilen bir yardımcı kütüphanesi oluşturdum. Bu yardımcı kitaplıktaki sınıf aşağıdaki gibi görünür:

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

} 

Çok basit, ancak test için yeterliydi. PrintDomain, yeni Uygulama Alanımda yaşadığını doğrulamak için orada. Derleme, bazı kaynak kodunu alır ve bir derleme oluşturmaya çalışır. Çalıştır, verilen kaynak koddan statik yöntemleri çalıştırmayı test etmemize izin verir. İşte

ben yardımcı kütüphane kullanımı aşağıda açıklanmıştır: Temel olarak, etki alanını oluşturur benim yardımcı sınıf (CompilerRunner) bir örneğini oluşturur Yeni takımını (gizli) derlemek için kullanır

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

, bazı kod çalışır Bu yeni derlemeden ve sonra belleği boşaltmak için etki alanını kaldırır.

MarshalByRefObject ve CreateInstanceFromAndUnwrap kullanımını fark edeceksiniz.Bunlar, yardımcı kütüphanenin gerçekten yeni alanda yaşadığından emin olmak için önemlidir.

Herhangi biri herhangi bir sorun farkederse veya bunu düzeltmek için önerileri varsa, bunları duymak isterim.

+2

Etki alanı sınırlarını geçmeye dikkat edilmesi gereken bir şey. MarshalByRefObject'ten marshalled türünü türetmezseniz, marshalling, value-value semantics kullanacaktır. Bu, alan sınırları boyunca iletişim çok konuşkan olacağı için, bazı çok yavaş yürütme ile sonuçlanabilir. Türlerinizi MarshalByRefObject'den türettiremiyorsanız, iletişim aracılığına aracılık eden MarshalByRefObject'ten türetilen ikincil Uygulama Etki Alanı'nda başlattığınız genel bir proxy nesnesi oluşturmak isteyebilirsiniz. MarshalByRefObject uygularsanız, nesne ömrü boyunca dikkatli olun ve nesneyi yeterince uzun süre canlı tutacak şekilde * InitializeLifetimeService * bir geçersiz kılma ... – jrista

+1

. Nesnenin sonsuza kadar devam etmesini istiyorsanız, null değerini InitializeLifetimeService'den döndürün. – jrista

+0

@jrista İşaretçi için teşekkürler. Yukarıdaki örnekte bulunan ulaştırılmış yazı tipi Hello sınıfı (this.assembly.GetType (typeName) ile erişilen)? – Nogwater

2

.NET 4.0 kadar bekleyebilir misiniz? Bununla birlikte kod-gen hafıza kaybı sorunu olmadan dinamik olarak kod oluşturmak için ifade ağaçları ve DLR kullanabilirsiniz.

Başka bir seçenek, .NET 3.5'i IronPython gibi dinamik bir dil ile kullanmaktır.

DÜZENLEME: İfade Ağacı Örneği

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

+0

Önerilerinizi beğendim, ancak maalesef bu projede benim için çalışmayacaklar (henüz .NET 4 kullanmıyoruz ve IronPython'u kullanamıyoruz (sadece C#)). Eğer sakıncası yoksa, ifade ağaçlarıyla ilgili cevabını düzeltebilir misin? Başka birine yardımcı olabilir. Bir dizede saklanan bir şeyi alıp bellekten boşaltılabilecek çalışma koduna dönüştürmek için kullanılabilirler mi? Teşekkürler. – Nogwater

+0

İfade ağaçlarındaki bir makaleye bağlantı ekledim. –

+3

Dinamik olarak DLR ile kod oluşturursanız, bunu çalıştırdığınızda alan kaplar. Bunu appdomains olmadan .Net 4'te serbest bırakabilir misiniz? Sorudaki bellek sızıntısı kod-geninden değil, aynı uygulama alanında (gerçekten bir sızıntı olmasa bile) boşaltılamayan bir derleme nedeniyle. –

İlgili konular