İmhası noktası olan yönetilmeyen kaynakları serbest bırakmak. Bir noktada yapılması gerekir, aksi takdirde asla temizlenmezler. Çöp toplayıcı bilmiyor nasıl çağırmak için DeleteHandle()
türünde bir değişkeni IntPtr
, bilmez olmadığını çağırması gerektiğine ya da değil DeleteHandle()
.
Not : Yönetilmeyen kaynak nedir? Microsoft .NET Framework'te bulduysanız: yönetilir. Eğer MSDN'yi kendiniz alaysanız, yönetilmez. .NET Framework'te kullanabileceğiniz her şeyin güzel ve rahat dünyasının dışına çıkmak için P / Invoke çağrılarını kullandığınız her şey yönetilmez - ve artık onu temizlemekle sorumlusunuz.
Oluşturduğunuz nesnenin, yönetilmeyen kaynakları temizlemek için dış dünyanın arayabileceği bazı yöntemleri ortaya çıkarması gerekir . Yöntem istediğiniz gibi adlandırılabilir:
public void Cleanup()
veya
public void Shutdown()
Ancak bunun yerine bu yöntem için standart bir ad vardır:
public void Dispose()
Oluşturulan bir arayüz bile vardı IDisposable
, bu sadece bir yönteme sahip:
public interface IDisposable
{
void Dispose()
}
Böylece nesnenizin IDisposable
arayüzü ortaya çıkarmasını sağlarsınız ve bu şekilde yönetilmeyen kaynaklarınızı temizlemek için bu tek yöntemi yazdığınıza söz verirsiniz:
public void Dispose()
{
Win32.DestroyHandle(this.CursorFileBitmapIconServiceHandle);
}
Ve işiniz bitti. Dışında daha iyisini yapabilirsin.
Nesneniz bir tür çerçeve arabelleği olarak 250MB System.Drawing.Bitmap (yani .NET tarafından yönetilen Bitmap sınıfı) ayırdıysa ne olur ? Elbette, bu yönetilen bir .NET nesnesidir ve çöp toplayıcı bunu serbest bırakır. Ama gerçekten orada otururken 250MB bellek bırakmak istiyor musunuz - çöp toplayıcının sonunda gelip serbest kalmasını bekliyor musunuz? Açık bir veritabanı bağlantısı varsa ne olur ? Şüphesiz bu bağlantının açık olmasını ve GC'nin nesneyi tamamlamasını beklemesini istemiyoruz.
Kullanıcı Dispose()
(artık nesneyi kullanmayı planlamıyor demektir) çağırdıysa, neden bu savurgan bitmapler ve veritabanı bağlantılarından kurtulmuyorsunuz?
Şimdi yapacağız:
- yönetilmeyen kaynaklardan kurtulmak (çünkü yapmak zorundayız) ve
- yönetilen kaynaklardan kurtulun (çünkü yardımcı olmak istiyoruz)
Bu Dispose()
yönetilen nesnelerden kurtulmak için yöntemimizi güncelleyelim :
public void Dispose()
{
//Free unmanaged resources
Win32.DestroyHandle(this.CursorFileBitmapIconServiceHandle);
//Free managed resources too
if (this.databaseConnection != null)
{
this.databaseConnection.Dispose();
this.databaseConnection = null;
}
if (this.frameBufferImage != null)
{
this.frameBufferImage.Dispose();
this.frameBufferImage = null;
}
}
Ve her şey iyi, sadece daha iyisini yapabilirsin !
Kişi nesnenizi çağırmayı unutursa ne olur Dispose()
? O zaman yönetilmeyen bazı kaynakları sızdırırlardı !
Not: Yönetilen kaynakları sızdırmazlar , çünkü sonunda çöp toplayıcı bir arka plan iş parçacığında çalışır ve kullanılmayan nesnelerle ilişkili belleği boşaltır. Bu, nesnenizi ve kullandığınız yönetilen nesneleri (ör. Bitmap
Ve DbConnection
) içerir.
Kişi aramayı unuttuysa Dispose()
, yine de pastırmasını kurtarabiliriz! Biz hala onu aramak için bir yol var için çöp toplayıcı nihayet kurtararak etrafında aldığında (yani sonlandırma) bizim nesne: Bunlardan.
Not: Çöp toplayıcı sonunda yönetilen tüm nesneleri serbest bırakır. Bunu yaptığında Finalize
, nesne üzerindeki yöntemi çağırır . GC biliyorum, ya da yaklaşık bakımı, gelmez senin bertaraf yöntemine. Bu, yönetilmeyen şeylerden kurtulmak istediğimizde çağırdığımız bir yöntem için seçtiğimiz bir isimdi.
Nesnemizin Çöp toplayıcı tarafından imha edilmesi, bu sinir bozucu yönetilmeyen kaynakları boşaltmak için mükemmel bir zamandır. Bunu Finalize()
yöntemi geçersiz kılarak yapıyoruz .
Not: C # 'da Finalize()
yöntemi açıkça geçersiz kılmazsınız. Bir C ++ yıkıcıya benzeyen bir yöntem yazarsınız ve derleyici bu yöntemi sizin uygulamanız olarak alır :Finalize()
~MyObject()
{
//we're being finalized (i.e. destroyed), call Dispose in case the user forgot to
Dispose(); //<--Warning: subtle bug! Keep reading!
}
Ama bu kodda bir hata var. Çöp toplayıcı bir arka plan iş parçacığında çalışır ; iki nesnenin yok olma sırasını bilmiyorsunuz. Dispose()
Kodunuzda, kurtulmaya çalıştığınız yönetilen nesnenin (yardımcı olmak istediğiniz için) artık orada olmaması tamamen mümkündür :
public void Dispose()
{
//Free unmanaged resources
Win32.DestroyHandle(this.gdiCursorBitmapStreamFileHandle);
//Free managed resources too
if (this.databaseConnection != null)
{
this.databaseConnection.Dispose(); //<-- crash, GC already destroyed it
this.databaseConnection = null;
}
if (this.frameBufferImage != null)
{
this.frameBufferImage.Dispose(); //<-- crash, GC already destroyed it
this.frameBufferImage = null;
}
}
Peki lüzum bir yoludur Finalize()
anlatmak için Dispose()
o gerektiğini herhangi yönetilen dokunma (onlar çünkü kaynaklar olmayabilir artık) hala yönetilmeyen kaynakları serbest bırakarak ederken,.
Bunu yapmak için standart örüntü bir üçüncü (!) Yöntemine sahip olmak Finalize()
ve Dispose()
her ikisini de çağırmaktır ; Burada Boole ifadesini geçtiğinizde (aksine ) çağırırsınız , yani ücretsiz yönetilen kaynaklar güvenli.Dispose()
Finalize()
Bu iç yöntem olabilir "CoreDispose" veya "MyInternalDispose" gibi bazı keyfi ad verilir, ama onu aramak için gelenektir edilebilir Dispose(Boolean)
:
protected void Dispose(Boolean disposing)
Ancak daha yararlı bir parametre adı şunlar olabilir:
protected void Dispose(Boolean itIsSafeToAlsoFreeManagedObjects)
{
//Free unmanaged resources
Win32.DestroyHandle(this.CursorFileBitmapIconServiceHandle);
//Free managed resources too, but only if I'm being called from Dispose
//(If I'm being called from Finalize then the objects might not exist
//anymore
if (itIsSafeToAlsoFreeManagedObjects)
{
if (this.databaseConnection != null)
{
this.databaseConnection.Dispose();
this.databaseConnection = null;
}
if (this.frameBufferImage != null)
{
this.frameBufferImage.Dispose();
this.frameBufferImage = null;
}
}
}
Ve IDisposable.Dispose()
yöntemi uygulamanızı şu şekilde değiştirirsiniz :
public void Dispose()
{
Dispose(true); //I am calling you from Dispose, it's safe
}
ve sonlandırıcınız:
~MyObject()
{
Dispose(false); //I am *not* calling you from Dispose, it's *not* safe
}
Not : Nesneniz uygulayan bir nesneden geliyorsa, Dispose'i geçersiz kıldığınızda temel Dispose yöntemini Dispose
çağırmayı unutmayın.
public override void Dispose()
{
try
{
Dispose(true); //true: safe to free managed resources
}
finally
{
base.Dispose();
}
}
Ve her şey iyi, sadece daha iyisini yapabilirsin !
Kullanıcı Dispose()
nesnenizi çağırırsa , her şey temizlendi. Daha sonra, çöp toplayıcı yanına gelip Sonlandır'ı çağırdığında, Dispose
tekrar arayacaktır .
Bu sadece israf etmekle kalmaz, aynı zamanda nesnenizin son çağrıdan Dispose()
attığınız nesnelere önemsiz referansları varsa, bunları tekrar atmaya çalışacaksınız!
Kodumda, attığım nesnelere yapılan başvuruları kaldırmaya dikkat ettiğimi fark edeceksiniz, bu yüzden Dispose
önemsiz bir nesne başvurusunu çağırmaya çalışmıyorum . Ama bu ince bir böceğin içeri sürülmesini engellemedi.
Kullanıcı çağırdığında Dispose()
: CursorFileBitmapIconServiceHandle tanıtıcısı yok edilir. Daha sonra çöp toplayıcı çalıştığında, aynı sapı tekrar yok etmeye çalışacaktır.
protected void Dispose(Boolean iAmBeingCalledFromDisposeAndNotFinalize)
{
//Free unmanaged resources
Win32.DestroyHandle(this.CursorFileBitmapIconServiceHandle); //<--double destroy
...
}
Bunu düzeltmenin yolu, çöp toplayıcıya nesneyi sonlandırmak için uğraşmaya gerek olmadığını söyler - kaynakları zaten temizlendi ve daha fazla çalışmaya gerek yok. Sen çağırarak bunu GC.SuppressFinalize()
içinde Dispose()
yöntemle:
public void Dispose()
{
Dispose(true); //I am calling you from Dispose, it's safe
GC.SuppressFinalize(this); //Hey, GC: don't bother calling finalize later
}
Şimdi kullanıcı aradı Dispose()
, biz var:
- serbest yönetilmeyen kaynaklar
- serbest yönetilen kaynaklar
GC'de sonlandırıcıyı çalıştırmanın bir anlamı yoktur - her şey halledilir.
Finalize'i yönetilmeyen kaynakları temizlemek için kullanamaz mıydım?
İçin belgeler Object.Finalize
diyor:
Sonlandırma yöntemi, nesne yok edilmeden önce geçerli nesne tarafından tutulan yönetilmeyen kaynaklarda temizleme işlemleri gerçekleştirmek için kullanılır.
Ancak MSDN belgeleri şunları da söylüyor IDisposable.Dispose
:
Yönetilmeyen kaynakların serbest bırakılması, serbest bırakılması veya sıfırlanmasıyla ilişkili uygulama tanımlı görevleri gerçekleştirir.
Peki hangisi? Yönetilmeyen kaynakları temizlemem için hangisi benim için? Cevap:
Bu senin seçimin! Ama seçin Dispose
.
Yönetilmeyen temizliğinizi kesinleştiriciye yerleştirebilirsiniz:
~MyObject()
{
//Free unmanaged resources
Win32.DestroyHandle(this.CursorFileBitmapIconServiceHandle);
//A C# destructor automatically calls the destructor of its base class.
}
Buradaki sorun, çöp toplayıcının nesnenizi ne zaman bitireceği konusunda hiçbir fikriniz yok. Yönetilmeyen, ihtiyaç duyulmayan, kullanılmayan yerel kaynaklarınız, çöp toplayıcı sonunda çalışana kadar değişecektir . Sonra sonlandırıcı yönteminizi çağırır; yönetilmeyen kaynakları temizleme. Object.Finalize dokümantasyonu şunları göstermektedir:
Sonlandırıcının yürütüldüğü kesin süre tanımsızdır. Sınıfınızın örnekleri için kaynakların kararlı bir şekilde serbest bırakılmasını sağlamak için, bir Kapat yöntemi uygulayın veya bir IDisposable.Dispose
uygulama sağlayın .
Bu, Dispose
yönetilmeyen kaynakları temizlemek için kullanmanın erdemidir ; yönetilmeyen kaynakların ne zaman temizlendiğini bilir ve kontrol edersiniz. Onların yok edilmesi "deterministik" tir .
Orijinal sorunuzu cevaplamak için: Neden GC bunu yapmaya karar verdiğinden ziyade hafızayı serbest bırakmıyorsunuz? Bunun yüz tanıma yazılımı ihtiyaçları iç görüntülerin 530 MB kurtulmak için şimdi artık gerekli olduğuna göre,. Yapmadığımız zaman: makine bir takas durağına öğütülür.
Bonus Okuma
Bu cevabın stilini (açıklayan seven herkes için neden bu kadar, ne kadar bariz hale gelir), sana Don Box'ın Essential COM Bölüm One okumak öneririz:
35 sayfada ikili nesneleri kullanmanın sorunlarını açıklar ve COM'u gözünüzün önünde icat eder. COM'un nedenini anladıktan sonra , kalan 300 sayfa açıktır ve Microsoft'un uygulamasını ayrıntılı olarak açıklar.
Bence herhangi bir nesne veya COM ile uğraşan her programcı en azından ilk bölümü okumalıdır. Bu şimdiye kadarki en iyi açıklamadır.
Ekstra Bonus Okuma
Bildiğiniz her şey Eric Lippert tarafından yanlış olduğunda
Bu nedenle doğru bir sonlandırıcı yazmak gerçekten çok zordur ve size verebileceğim en iyi tavsiye denememektir .