2009-09-24 22 views
15

Mam obiekt, który żyje wiecznie. Usuwam wszystkie odnośniki, które widzę, do niego po użyciu, ale nadal nie są gromadzone. Jego cykl życia jest dość skomplikowany, więc nie mogę być pewny, że wszystkie referencje zostały usunięte.Znajdź odniesienia do obiektu w czasie wykonywania

if (container.Controls.Count > 0) 
{ 
    var controls = new Control[ container.Controls.Count ]; 
    container.Controls.CopyTo(controls, 0); 

    foreach (var control in controls) 
    { 
     container.Controls.Remove(control); 
     control.Dispose(); 
    } 

    controls = null; 
} 

GC.Collect(); 
GC.Collect(1); 
GC.Collect(2); 
GC.Collect(3); 

Jak mogę się dowiedzieć, jakie są jeszcze odniesienia? Dlaczego nie jest on gromadzony?

+0

Pokaż nam swój kod, a być może będziemy mogli pomóc. Pamiętaj, że odśmiecanie niekoniecznie musi nastąpić natychmiast. – Lazarus

+0

I domyślam się, że prawdziwe pytanie brzmi: dlaczego się o to martwisz? Jeśli korzystasz z zasobów jednorazowych, usuń je, gdy ich już nie używasz, usuwaj niezarządzane zasoby systemowe i uważaj, korzystając ze strun interwencyjnych. –

+0

Kod jest: if (container.Controls.Count> 0) { \t \t \t \t sterujące var = Nowa kontrola [container.Controls.Count]; \t \t \t \t container.Controls.CopyTo (sterowanie, 0); \t \t \t \t foreach (regulowana zmienna w grupie kontrolnej) { \t \t \t \t \t container.Controls.Remove (próba kontrolna); \t \t \t \t \t control.Dispose(); \t \t \t \t \t \t \t \t \t} \t \t \t \t Kontrola = NULL; \t \t \t} GC.Collect(); GC.Collect (1); GC.Collect (2); GC.Collect (3); Ale wciąż jest w pamięci. Więc to znaczy, że stal ma korzenie. Jak mogę znaleźć te korzenie? –

Odpowiedz

11

Spróbuj użyć profilera pamięci , (np. ants), który powie Ci, co utrzymuje obiekt przy życiu. Próba drugiego odgadnięcia tego typu problemu jest bardzo trudna.

Red-gate daje 14-dniową wersję próbną, która powinna być dłuższa niż wystarczająco, aby rozwiązać ten problem i zdecydować, czy profiler pamięci zapewnia długoterminową wartość.

Istnieje wiele innych profilowania pamięci dostępnych na rynku (np .NET Memory Profiler) większość z nich bezpłatne wersje próbne, jednak odkryłem, że narzędzia Red-Gate są łatwe w użyciu, więc raczej próbować je w pierwszej kolejności.

+0

Dzięki, spróbuję! –

+0

Mają też darmowe filmy szkoleniowe (i dokumenty) itp., Które wyjaśniają, jak działa garbage collecter .net, który może ci się przydać. –

+0

@ er-v: obiekt mógł zostać zebrany, ale pamięć mogła nie zostać odzyskana przez system Windows. Struktura nie musi zwracać pamięci do systemu operacyjnego. – user7116

0

Nie jest ono gromadzone, ponieważ nie usunięto wszystkich odniesień do niego. GC będzie oznaczać obiekty do zbierania tylko wtedy, gdy nie mają korzeni w aplikacji.

Jakich środków używasz do sprawdzania GC, aby sprawdzić, czy odebrał obiekt?

2

Zbiór śmieci w .NET nie jest schematem liczenia (np. COM), ale implementacją znakowania i odejmowania. Zasadniczo, GC działa w "losowych" czasach, gdy odczuwa taką potrzebę, a zbieranie obiektów nie jest zatem deterministyczne.

Można jednak ręcznie uruchomić kolekcję (GC.Collect()), ale może być konieczne poczekanie na uruchomienie finalizatorów (GC.WaitForPendingFinalizers()). Jednak robienie tego w aplikacji produkcyjnej jest odradzane, ponieważ może wpływać na wydajność zarządzania pamięcią (GC działa zbyt często lub czeka na uruchomienie finalizatorów). Jeśli obiekt nadal istnieje, to wciąż ma gdzieś jakieś odniesienie na żywo.

3

Będziesz musiał użyć rozszerzenia Windbg i Sosex.

Komendy !DumpHeap i !GCRoot mogą pomóc w zidentyfikowaniu instancji i wszystkich pozostałych odniesień, które utrzymują ją przy życiu.

3

Używam .NET Memory Profiler do wykonywania poważnych profilów pamięci w jednym z naszych projektów. To doskonałe narzędzie do zarządzania pamięcią aplikacji. Nie otrzymuję zapłaty za te informacje :) ale to mi bardzo pomogło.

+0

Uzgodnione, jest to bardzo przydatne narzędzie, zwłaszcza wizualne. –

3

Rozwiązałem podobny problem z rozszerzeniem SOS (które najwyraźniej już nie działa z Visual Studio 2013, ale działa dobrze ze starszymi wersjami Visual Studio).

użyłem następujący kod, aby uzyskać adres obiektu, dla którego chciałem śledzić nazwy:

public static string GetAddress(object o) 
{ 
    if (o == null) 
    { 
     return "00000000"; 
    } 
    else 
    { 
     unsafe 
     { 
      System.TypedReference tr = __makeref(o); 
      System.IntPtr ptr = **(System.IntPtr**) (&tr); 
      return ptr.ToString ("X"); 
     } 
    } 
} 

a następnie w Visual Studio 2012 okienku bezpośrednim, przy uruchomionej w debugger, typ:

.load C:\Windows\Microsoft.NET\Framework\v4.0.30319\sos.dll 

który załaduje rozszerzenie SOS.dll.

Następnie można użyć GetAddress(x) uzyskać szesnastkowy adres obiektu (na przykład 8AB0CD40), a następnie użyć:

!do 8AB0CD40 
!GCRoot -all 8AB0CD40 

zrzucić obiekt i znaleźć wszystkie odwołania do obiektu.

Należy pamiętać, że jeśli działa GC, może zmienić adres obiektu.

Powiązane problemy