C# Language
Garbage Collector in .Net
Szukaj…
Zagęszczanie dużych obiektów
Domyślnie stos dużych obiektów nie jest kompaktowany, w przeciwieństwie do klasycznego stosu obiektów, który może prowadzić do fragmentacji pamięci, a ponadto może prowadzić do OutOfMemoryException
Począwszy od .NET 4.5.1 istnieje możliwość jawnego kompaktowania sterty dużych obiektów (wraz z odśmiecaniem):
GCSettings.LargeObjectHeapCompactionMode = GCLargeObjectHeapCompactionMode.CompactOnce;
GC.Collect();
Podobnie jak każde jawne żądanie wyrzucania elementów bezużytecznych (nazywane jest żądaniem, ponieważ CLR nie jest zmuszone do jego wykonania), używaj go ostrożnie i domyślnie unikaj go, jeśli możesz, ponieważ może on skalibrować statystyki GC
, zmniejszając jego wydajność.
Słabe referencje
W .NET GC przydziela obiekty, gdy nie ma do nich żadnych odniesień. Dlatego, podczas gdy do obiektu można jeszcze dotrzeć z kodu (istnieje silne odniesienie do niego), GC nie przydzieli tego obiektu. Może to stanowić problem, jeśli istnieje wiele dużych obiektów.
Słaba referencja to referencja, która pozwala GC na zbieranie obiektu, jednocześnie pozwalając na dostęp do obiektu. Słaba referencja jest ważna tylko w nieokreślonym czasie, dopóki obiekt nie zostanie zebrany, gdy nie ma silnych referencji. Gdy używasz słabego odwołania, aplikacja może nadal uzyskać silne odniesienie do obiektu, co zapobiega gromadzeniu go. Tak słabe referencje mogą być przydatne do trzymania dużych obiektów, których inicjowanie jest kosztowne, ale powinno być dostępne do wyrzucania elementów bezużytecznych, jeśli nie są aktywnie używane.
Proste użycie:
WeakReference reference = new WeakReference(new object(), false);
GC.Collect();
object target = reference.Target;
if (target != null)
DoSomething(target);
Słabe referencje mogłyby być wykorzystane na przykład do przechowywania bufora obiektów. Należy jednak pamiętać, że zawsze istnieje ryzyko, że śmieciarz dotrze do obiektu przed ponownym ustanowieniem silnego odwołania.
Słabe referencje są również przydatne do unikania wycieków pamięci. Typowy przypadek użycia dotyczy zdarzeń.
Załóżmy, że mamy jakiś moduł obsługi zdarzenia w źródle:
Source.Event += new EventHandler(Handler)
Ten kod rejestruje moduł obsługi zdarzeń i tworzy silne odniesienie ze źródła zdarzenia do obiektu nasłuchującego. Jeśli obiekt źródłowy ma dłuższą żywotność niż detektor, a detektor nie potrzebuje już zdarzenia, gdy nie ma innych odniesień do niego, użycie normalnych zdarzeń .NET powoduje wyciek pamięci: obiekt źródłowy przechowuje obiekty detektora w pamięci, która powinny być zbierane śmieci.
W takim przypadku dobrym pomysłem może być użycie Wzorca Słabego Zdarzenia .
Coś jak:
public static class WeakEventManager
{
public static void SetHandler<S, TArgs>(
Action<EventHandler<TArgs>> add,
Action<EventHandler<TArgs>> remove,
S subscriber,
Action<S, TArgs> action)
where TArgs : EventArgs
where S : class
{
var subscrWeakRef = new WeakReference(subscriber);
EventHandler<TArgs> handler = null;
handler = (s, e) =>
{
var subscrStrongRef = subscrWeakRef.Target as S;
if (subscrStrongRef != null)
{
action(subscrStrongRef, e);
}
else
{
remove(handler);
handler = null;
}
};
add(handler);
}
}
i używane w ten sposób:
EventSource s = new EventSource();
Subscriber subscriber = new Subscriber();
WeakEventManager.SetHandler<Subscriber, SomeEventArgs>(a => s.Event += a, r => s.Event -= r, subscriber, (s,e) => { s.HandleEvent(e); });
W tym przypadku oczywiście mamy pewne ograniczenia - zdarzenie musi być
public event EventHandler<SomeEventArgs> Event;
Jak sugeruje MSDN :
- Używaj długich słabych odniesień tylko wtedy, gdy jest to konieczne, ponieważ stan obiektu jest nieprzewidywalny po sfinalizowaniu.
- Unikaj używania słabych odniesień do małych obiektów, ponieważ sam wskaźnik może być tak duży lub większy.
- Unikaj używania słabych referencji jako automatycznego rozwiązania problemów z zarządzaniem pamięcią. Zamiast tego opracuj skuteczne zasady buforowania do obsługi obiektów aplikacji.