Chciałbym poświęcić kilka postów na opisanie WeakReference oraz praktycznych przykładów wykorzystania tej klasy. W dzisiejszym wpisie strona teoretyczna i opis klasy.
Referencje w środowisku .NET można podzielić na słabe (weak references) oraz silne\mocne (strong references). Silne referencje są wszystkim doskonale znane np:
var sampleClass = new SampleClass();
Innymi słowy jest to standardowy typ powiązania. W momencie gdy wszystkie silne referencje zostaną usunięte wtedy GC może zacząć usuwać taki obiekt z pamięci:
var sampleClass = new SampleClass(); sampleClass = null // w tej chwili nie ma silnych referencji i GC // w odpowiednim czasie usunie zasoby z pamięci.
Słabą referencje można utworzyć wyłącznie za pomocą klasy WeakReference. GC określa obiekt jako niepotrzebny gdy wszystkie silne referencje zostaną usunięte. Nie bierze pod uwagę słabych referencji. Z tego względu jest możliwe posiadanie weak reference do obiektu, który jest uznawany przez GC jako niepotrzebny i może zostać w każdej chwili usunięty. Przykład definiowania słabej referencji do obiektu SampleClass:
WeakReference reference=new WeakReference(new SampleClass("text")); var sampleClass = (SampleClass) reference.Target; if(sampleClass==null) Console.WriteLine("Obiekt został usuniety przez GC"); else Console.WriteLine("Obiekt wciąż istnieje:{0}",sampleClass.Text);
Weak reference definiuje się poprzez stworzenie instancji klasy WeakReference i przekazanie w konstruktorze danego obiektu. Właściwość Target zawiera wskazywany obiekt. Gdy GC usunięcie obiekt wtedy Target zwraca NULL. W przeciwnym przypadku właściwość zwróci wskazywany obiekt, w naszym przypadku jest to SampleClass.
Poświeciłem kiedyś kilka postów o działaniu GC. Wynika z nich, że GC nie usuwa pamięci natychmiast gdy wszystkie silne referencje zostały wyzerowane. Nie da się określić momentu w którym pamięć fizycznie zostanie zwolniona na rzecz innego obiektu. Wszystko zależy od wielu czynników jak np. częstość alokowania obiektów czy przede wszystkim aktualne obciążenie pamięci.
Klasa GC ma metodę Collect wymuszającą usunięcie niepotrzebnych elementów. Zwykle odradzam stosowanie tej metody ale na potrzeby dzisiejszego wpisu warto wymusić to aby zobaczyć, że słaba referencja nie jest brana pod uwagę przez GC:
WeakReference reference=new WeakReference(new SampleClass("text")); GC.Collect(); var sampleClass = (SampleClass) reference.Target; if(sampleClass==null) Console.WriteLine("Obiekt został usuniety przez GC"); else Console.WriteLine("Obiekt wciąż istnieje:{0}",sampleClass.Text);
W tej chwili na ekranie powinna pojawić się wiadomość, że obiekt został usunięty przez GC. WeakReference zawiera również właściwość IsAlive dzięki której możemy sprawdzić czy obiekt nie został usunięty:
WeakReference reference=new WeakReference(new SampleClass("text")); GC.Collect(); Console.WriteLine(reference.IsAlive);
Czy to znaczy, że możemy napisać kod następująco?
WeakReference reference=new WeakReference(new SampleClass("text")); if(reference.IsAlive) Console.WriteLine(((SampleClass)reference.Target).Text); else Console.WriteLine("Obiekt został usuniety.");
Niestety nie… Gdy IsAlive zwraca false wtedy zawsze mamy pewność, że obiekt już nie istnieje. Natomiast gdy właściwość zwróci True wtedy powyższy kod nie jest thread-safe ponieważ co jeśli obiekt zostanie usunięty w momencie odczytu Target (czyli IsAlive zwrócił true a potem natychmiast obiekt został usunięty)? W celu wyjaśnienia problemu dodałem komentarz do powyższego antywzorca:
WeakReference reference=new WeakReference(new SampleClass("text")); if(reference.IsAlive) //T1: W tej chwili obiekt jeszcze istnieje więc // przechodzimy dalej. { //T2: Obiekt został w międzyczasie usunięty. //Target zawiera NULL! Console.WriteLine(((SampleClass)reference.Target).Text); }
Witam
Do pełni szczęścia brakuje informacji: na co komu takie cudo? 🙂
Również mnie ciekawi w czym znajdziemy zastosowanie używania Weakreference
Juz w nastpenym poscie praktyczny przyklad:)
Czekamy 🙂
Przydaje się w bardzo dziwnych przypadkach związanych z optymalizacją pamięci. Miałem okazję musieć użyć WeakReference do sprytnego odpinania StatycznychEventów (w projekcie liczonym w setkach tysięcy linii kodu, jak nie miliony) gdzie nie bardzo dało się znaleźć miejsce bezpiecznego odpięcia eventów.
Zastosowanie? np. implementacja cache
IMHO: Do cache akurat lepiej wykorzystac gotowe do tego klasy w .NET 4.0 ktore umozliwaja okreslenie czasu keszowania itp.
Są ludziki, którzy zadają to pytanie na rozmowach kwalifikacyjnych, choć dla mnie to pytanie nic nie weryfikuje.
Dla tych cho nie wiedzą po co komuś takie cudo 😉
To często stosuje sie we wzorcu Event Agregator. Takie cudo jest np. wykorzystywane w mvvm light do obslugi komunikatow wpf
Ten przykład zwraca, że obiekt wciąż istnieje.
Jeżeli się usunie poniższe odwołania do sampleClass to GC usunie obiekt, ale wynik tego można zobaczyć tylko przy debugowaniu. Nie wiem czy ktoś to testował czy wszyscy uwierzyli na słowo?
//Dodając to do kodu otrzymamy sampleClass != null
if(sampleClass==null)
Console.WriteLine(“Obiekt został usuniety przez GC”);
else
Console.WriteLine(“Obiekt wciąż istnieje:{0}”,sampleClass.Text);
Druga sprawa to odpowiedź na pytanie:
Czy to znaczy, że możemy napisać kod następująco?
Niestety nie… Gdy IsAlive zwraca false…
Brzmi to tak jakby zawsze można było stosować wcześniejszy warunek:
if(sampleClass==null)
a to przecież bez różnicy. Jedno i drugie może przestać istnieć. Chyba że został popełniony jakiś błąd w założeniach i zawsze jest != null.