Microsoft belgelerini](https://docs.microsoft.com/dotnet/api/system.idisposable) okuyarak `IDisposable' arayüzünün "birincil" kullanımının yönetilmeyen kaynakları temizlemek olduğunu biliyorum.
Bana göre, "yönetilmeyen" veritabanı bağlantıları, soketler, pencere tutamaçları vb. gibi şeyler anlamına gelir. Ancak, Dispose()
yönteminin managed kaynaklarını serbest bırakmak için uygulandığı kodlar gördüm, bu bana gereksiz görünüyor, çünkü çöp toplayıcı bunu sizin için halletmelidir.
Örneğin:
public class MyCollection : IDisposable
{
private List<String> _theList = new List<String>();
private Dictionary<String, Point> _theDict = new Dictionary<String, Point>();
// Die, clear it up! (free unmanaged resources)
public void Dispose()
{
_theList.clear();
_theDict.clear();
_theList = null;
_theDict = null;
}
Sorum şu: Bu, çöp toplayıcının MyCollection
tarafından kullanılan belleği normalde olduğundan daha hızlı boşaltmasını sağlar mı?
Düzenle: Şimdiye kadar insanlar veritabanı bağlantıları ve bitmapler gibi yönetilmeyen kaynakları temizlemek için IDisposable kullanımına ilişkin bazı iyi örnekler yayınladılar. Ancak yukarıdaki koddaki _theList
in bir milyon string içerdiğini ve çöp toplayıcıyı beklemek yerine bu belleği şimdi boşaltmak istediğinizi varsayalım. Yukarıdaki kod bunu başarır mıydı?
IDisposablegenellikle
using` deyiminden yararlanmak ve yönetilen nesnelerin deterministik temizliğini yapmanın kolay bir yolundan yararlanmak için kullanılır.
public class LoggingContext : IDisposable {
public Finicky(string name) {
Log.Write("Entering Log Context {0}", name);
Log.Indent();
}
public void Dispose() {
Log.Outdent();
}
public static void Main() {
Log.Write("Some initial stuff.");
try {
using(new LoggingContext()) {
Log.Write("Some stuff inside the context.");
throw new Exception();
}
} catch {
Log.Write("Man, that was a heavy exception caught from inside a child logging context!");
} finally {
Log.Write("Some final stuff.");
}
}
}
Evet, bu kod tamamen gereksiz ve gereksizdir ve çöp toplayıcının başka türlü yapmayacağı bir şey yapmasını sağlamaz (MyCollection'ın bir örneği kapsam dışına çıktığında, yani.) Özellikle .Clear()
çağrıları.
Düzenlemenize cevap: Sayılır. Eğer bunu yaparsam:
public void WasteMemory()
{
var instance = new MyCollection(); // this one has no Dispose() method
instance.FillItWithAMillionStrings();
}
// 1 million strings are in memory, but marked for reclamation by the GC
Bellek yönetimi açısından işlevsel olarak bununla aynıdır:
public void WasteMemory()
{
var instance = new MyCollection(); // this one has your Dispose()
instance.FillItWithAMillionStrings();
instance.Dispose();
}
// 1 million strings are in memory, but marked for reclamation by the GC
Belleği şu anda gerçekten boşaltmanız gerekiyorsa, GC.Collect()
işlevini çağırın. Ancak burada bunu yapmak için bir neden yok. Bellek, ihtiyaç duyulduğunda serbest bırakılacaktır.
Eğer MyCollection
zaten çöp olarak toplanacaksa, onu atmanıza gerek yoktur. Bunu yapmak CPU'yu gereğinden fazla yorar ve hatta çöp toplayıcının önceden hesapladığı bazı analizleri geçersiz kılabilir.
Yönetilmeyen kaynaklarla birlikte iş parçacıklarının doğru şekilde atıldığından emin olmak gibi şeyler yapmak için IDisposable
kullanıyorum.
EDIT Scott'ın yorumuna yanıt olarak:
GC performans ölçümlerinin etkilendiği tek zaman [sic] GC.Collect() çağrısının yapıldığı zamandır"
Kavramsal olarak, GC nesne referans grafiğinin bir görünümünü ve iş parçacıklarının yığın çerçevelerinden ona yapılan tüm referansları tutar. Bu yığın oldukça büyük olabilir ve birçok bellek sayfasına yayılabilir. Bir optimizasyon olarak GC, sayfayı gereksiz yere yeniden taramaktan kaçınmak için çok sık değişmesi muhtemel olmayan sayfaların analizini önbelleğe alır. GC, bir sayfadaki veriler değiştiğinde çekirdekten bildirim alır, böylece sayfanın kirli olduğunu ve yeniden taranması gerektiğini bilir. Koleksiyon Gen0'daysa, sayfadaki diğer şeylerin de değişiyor olması muhtemeldir, ancak Gen1 ve Gen2'de bu daha az olasıdır. Anekdot olarak, Silverlight eklentisinin bu platformda çalışmasını sağlamak için GC'yi Mac'e taşıyan ekip için bu kancalar Mac OS X'te mevcut değildi.
Kaynakların gereksiz yere harcanmasına karşı bir başka nokta: bir sürecin boşaltıldığı bir durum düşünün. Sürecin bir süredir çalıştığını da düşünün. Büyük olasılıkla bu sürecin bellek sayfalarının çoğu diske takas edilmiştir. En azından artık L1 ya da L2 önbellekte değillerdir. Böyle bir durumda, yükü boşaltmakta olan bir uygulamanın, süreç sonlandırıldığında zaten işletim sistemi tarafından serbest bırakılacak olan kaynakları 'serbest bırakmak' için tüm bu veri ve kod sayfalarını belleğe geri takas etmesinin bir anlamı yoktur. Bu, yönetilen ve hatta bazı yönetilmeyen kaynaklar için geçerlidir. Yalnızca arka planda çalışmayan iş parçacıklarını canlı tutan kaynaklar atılmalıdır, aksi takdirde süreç canlı kalacaktır.
Şimdi, normal yürütme sırasında, yönetilmeyen bellek sızıntılarını önlemek için doğru şekilde temizlenmesi gereken geçici kaynaklar vardır (@fezmonkey'in belirttiği gibi veritabanı bağlantıları, soketler, pencere tutamaçları). Bunlar atılması gereken türden şeylerdir. Eğer bir iş parçacığının sahibi olan bir sınıf yaratırsanız (sahibi derken onu yarattığını ve bu nedenle durmasını sağlamaktan sorumlu olduğunu kastediyorum, en azından benim kodlama stilime göre), o zaman bu sınıf büyük olasılıkla IDisposable
uygulamalı ve Dispose
sırasında iş parçacığını yıkmalıdır.
NET çerçevesi IDisposable
arayüzünü, geliştiricilere bu sınıfın must dispose_ edilmesi gerektiğine dair bir sinyal, hatta uyarı olarak kullanır. Çerçevede IDisposable
ı uygulayan (açık arayüz uygulamaları hariç) ve atmanın isteğe bağlı olduğu herhangi bir tür düşünemiyorum.