2012-06-27 8 views
7

Powiedz, że masz klasę z właściwością zdarzenia. Jeśli tworzysz tę klasę w kontekście lokalnym, bez zewnętrznych odniesień, czy przypisanie wyrażenia lambda do zdarzenia uniemożliwi stanie się zbiorem śmieci?Czy obiekty lambda przypisane do zdarzenia uniemożliwiają wyrzucanie śmieci z obiektu będącego właścicielem?

{ 
    var o = new MyClass(); 
    o.MyClassEvent += (args) => {}; 
} 
// Will 'o' be eligible for garbage collection here? 
+1

Powinien kwalifikować się do GC. Nie jestem tego pewien, aby to była odpowiedź. –

+0

Nie rozumiem, dlaczego miałoby to wyglądać inaczej niż subskrybowanie jakiejkolwiek innej funkcji (lambda lub innej) do zdarzenia. Zgadzam się z Timem. –

+0

możliwy duplikat [Garbage collection podczas używania anonimowych delegatów do obsługi zdarzeń] (http://stackoverflow.com/questions/371109/garbage-collection-when-using-anonymous-delegates- for-event-handling) –

Odpowiedz

5

Nie, o zostanie zwolniony, podobnie jak funkcja lambda. Nie ma żadnych odniesień do o z jakiegokolwiek innego miejsca, więc nie ma powodu, aby nie został zwolniony.

1

Gdy środowisko wykonawcze pozostawia ten blok kodu, nie ma już żadnych odwołań do obiektu, więc zbiera się śmieci.

+0

Skąd wiadomo, że nie ma już odwołań do tego magazynu lokalnego? –

+0

Nie ma znaczenia, czy istnieją odniesienia do czegoś. W rzeczywistości lambda jest prawdopodobnie obiektem alokowanym stosem z odniesieniem do tego obiektu, więc twoje roszczenie jest fałszywe. Dla GC ważne jest to, czy obiekt jest * osiągalny *. – delnan

0

To zależy od tego, co dokładnie rozumiesz przez "kontekst lokalny". Porównaj:

static void highMem() 
{ 
    var r = new Random(); 
    var o = new MyClass(); 
    o.MyClassEvent += a => { }; 
} 
static void Main(string[] args) 
{ 
    highMem(); 
    GC.Collect(); //yes, it was collected 
} 

Do:

static void Main(string[] args) 
{ 
    var r = new Random(); 
    { 
     var o = new MyClass(); 
     o.MyClassEvent += a => { }; 
    } 
    GC.Collect(); //no, it wasn't collected 
} 

w moim otoczeniu (debugowania, VS 2010, Win7 x64), pierwszy z nich kwalifikuje się do GC, a drugi nie był (jak zaznaczone posiadanie MyClass zajmuje 200 MB pamięci i sprawdzanie w Menedżerze zadań), mimo że nie mieści się w zakresie. Przypuszczam, że dzieje się tak dlatego, że kompilator deklaruje wszystkie zmienne lokalne na początku metody, więc do CLR, o nie jest poza zakresem, nawet jeśli nie można go użyć w kodzie C#.

+1

'o' jest poza zakresem, ale zakres ma niewiele wspólnego z odśmiecaniem, odnośniki na żywo są. Jeśli obszar stosu odniesienia jest używany, gdy odwołanie znajduje się jeszcze w zakresie, można go zebrać. Może nawet zostać zebrany przed zakończeniem operacji, w niektórych przypadkach. –

2

Krótka odpowiedź, nie, o zostanie zwolniona. Nie przejmuj się tym.

Nieco dłużej odpowiedź:

Twój kod ma mniej lub więcej z następujących czynności:

  1. Tworzenie trochę pamięci lokalnej na tym wątku przez odniesienie do nowych MojaKlasa (o).
  2. Utwórz nową MyClass
  3. Zapisz referencję do nowej MyClass pod numerem o.
  4. Utwórz nowego delegata od lambda.
  5. Przypisz, że delegat na wydarzenie (o ma teraz odniesienie do delegata).

W dowolnym momencie GC może przerwać wątki, a następnie sprawdzić, które obiekty są lub nie są zrootowane. Korzenie są obiektami, które są statyczne lub znajdują się w lokalnej pamięci wątku (przez co rozumiem zmienne lokalne w danym wątku, zaimplementowane przez stos, a nie "Wątek-lokalny magazyn", który jest w zasadzie formą statyczną). Obiekty ukorzenione to korzenie, obiekty, do których się odwołują, obiekty, do których się odwołują, i tak dalej. Zrootowane obiekty nie będą zbierane, reszta będzie (z wyjątkiem pewnych dodatkowych rzeczy związanych z finalizatorami, które na razie zignorujemy).

W żadnym momencie po utworzeniu obiektu MyClass nie został on zakorzeniony w lokalnej pamięci wątku.

W żadnym momencie po utworzeniu obiektu delegata nie zostało ono zakorzenione w lokalnej pamięci wątku lub przez odnośnik do MyClass.

Co dalej?

To zależy. Lambda nie utrzyma przy życiu MyClass (MyClass utrzymuje go przy życiu, ale kiedy MyClass idzie, tak robi lambda). To nie wystarczy, aby odpowiedzieć, że "Czy" i "kwalifikujesz się do zbierania śmieci?" chociaż.

void Meth0() 
{ 
    var o = new MyClass(); 
    o.MyClassEvent += (args) => {}; 
}//o is likely eligible for collection, though it doesn't have to be. 

void Meth1() 
{ 
    int i = 0; 
    { 
    var o = new MyClass(); 
    o.MyClassEvent += (args) => {}; 
    }//o is likely not eligible for collection, though it could be. 
    while(i > 100000000);//just wasting time 
} 

void Meth2() 
{ 
    { 
    var o = new MyClass(); 
    o.MyClassEvent += (args) => {}; 
    int i = 0;//o is likely eligible for collection, though it doesn't have to be. 
    while(i > 100000000);//just wasting time 
    } 
} 

void Meth3() 
{ 
    { 
    var o = new MyClass(); 
    o.MyClassEvent += (args) => {}; 
    var x0 = new MyClass();//o is even more likely eligible for collection, though it doesn't have to be. 
    var x1 = new MyClass();//o is even more likely eligible for collection, though it doesn't have to be. 
    var x2 = new MyClass();//o is even more likely eligible for collection, though it doesn't have to be. 
    var x3 = new MyClass();//o is even more likely eligible for collection, though it doesn't have to be. 
    var x4 = new MyClass();//o is even more likely eligible for collection, though it doesn't have to be. 
    var x5 = new MyClass();//o is even more likely eligible for collection, though it doesn't have to be. 
    var x6 = new MyClass();//o is even more likely eligible for collection, though it doesn't have to be. 
    var x7 = new MyClass();//o is even more likely eligible for collection, though it doesn't have to be. 
    var x8 = new MyClass();//o is even more likely eligible for collection, though it doesn't have to be. 
    var x9 = new MyClass();//o is even more likely eligible for collection, though it doesn't have to be. 
    var x10 = new MyClass();//o is even more likely eligible for collection, though it doesn't have to be. 
    var x11 = new MyClass();//o is even more likely eligible for collection, though it doesn't have to be. 
    int i = 0; 
    while(i > 100000000);//just wasting time 
    } 
} 

Meth0 jest pozornie najprostsze, ale w rzeczywistości nie jest, więc wrócimy do niego.

W Meth1, wdrożenie jest prawdopodobne być pozostawiając pamięci lokalnej, jak to jest, ponieważ nie ma już potrzebny, więc podczas o nie jest w zakresie, wdrożenie będzie jeszcze że lokalne przechowywanie w użyciu.

W Meth2, realizacja mógłby korzystać z tego samego magazynu lokalnego go używał do o dla i więc mimo że wciąż jest w zakresie, byłoby uprawnione do zbierania („zasięg” oznacza, że ​​programista mógł wybrać do coś z tym, ale on lub ona nie, a skompilowany kod nie potrzebuje takiej koncepcji).

Metoda Meth3 jest bardziej prawdopodobna do ponownego wykorzystania pamięci masowej, ponieważ dodatkowe tymczasowe jej wykorzystanie sprawia, że ​​ta implementacja zamiast na początku ustawiać pamięć masową, ma więcej sensu.

Żadna z tych rzeczy ma być w ten sposób. Met2 i Meth3 mogą od samego początku odłożyć na bok całą pamięć potrzebną dla tej metody. Meth1 może ponownie wykorzystać pamięć masową, ponieważ nie ma znaczenia, czy zostaną ponownie przypisane numery i i o.

Met0 jest bardziej skomplikowany, ponieważ może zależeć od tego, co robi metoda wywołująca w połączeniu z pamięcią lokalną, a nie od czyszczenia w czasie (oba są uzasadnionymi implementacjami). IIRC zawsze jest czyste z bieżącą implementacją, ale nie jestem pewien i to nie ma znaczenia.

Podsumowując, zakres nie jest istotną kwestią, ale to, czy kompilator, a później JITter, może i nie korzysta z pamięci lokalnej powiązanej z obiektem. Możliwe jest nawet wyczyszczenie obiektu przed wywołaniem metod i właściwości (jeśli te metody i właściwości nie wykorzystują wartości this ani żadnego z pól obiektu, ponieważ będzie działało dobrze, jeśli obiekt został usunięty!).

Powiązane problemy