20 Stimmen

Eignet sich WeakReference als Cache?

Ich habe einen Cache, der WeakReferences zu den gecachten Objekten verwendet, um sie automatisch aus dem Cache zu entfernen, wenn der Speicher knapp wird. Mein Problem ist, dass die zwischengespeicherten Objekte sehr bald nach ihrer Speicherung im Cache abgeholt werden. Der Cache läuft in einer 64-Bit Anwendung und obwohl noch mehr als 4gig Speicher zur Verfügung stehen, werden alle gecachten Objekte abgeholt (sie befinden sich zu diesem Zeitpunkt normalerweise im G2-Heap). Es gibt keine manuell ausgelöste Garbage Collection, wie der Prozess-Explorer zeigt.

Welche Methoden kann ich anwenden, um die Objekte ein wenig länger leben zu lassen?

18voto

jrista Punkte 31522

Die Verwendung von WeakReferences als primäres Mittel zur Referenzierung von zwischengespeicherten Objekten ist nicht wirklich eine gute Idee, denn wie Josh schon sagte, ist man allen zukünftigen Verhaltensänderungen an WeakReference und dem GC ausgeliefert.

Wenn Ihr Cache jedoch irgendeine Art von Wiederauferstehungsfähigkeit benötigt, ist die Verwendung von WeakReferences für Elemente, die zur Bereinigung anstehen, nützlich. Wenn ein Objekt die Auslagerungskriterien erfüllt, ändern Sie seine Referenz in eine schwache Referenz, anstatt es sofort auszulagern. Wenn es vor der GC angefordert wird, stellen Sie seine starke Referenz wieder her, und das Objekt kann wieder leben. Ich habe dies für einige Caches als nützlich empfunden, die schwer vorhersehbare Trefferquoten aufweisen und deren "Wiederbelebung" häufig genug ist, um von Vorteil zu sein.

Wenn Sie vorhersehbare Trefferquotenmuster haben, würde ich auf die Option WeakReference verzichten und explizite Auslagerungen vornehmen.

0 Stimmen

Das Problem ist, dass sobald ich einen starken Verweis auf ein zwischengespeichertes Objekt habe, die Gefahr besteht, dass mir der Speicher ausgeht, bevor ich es als abholbar festlegen kann.

2 Stimmen

Das ist die Aufgabe Ihrer Räumungsstrategie. Jeder Cache braucht vor allem zwei Dinge: die Möglichkeit, eine Instanz im Cache zu registrieren, und einen Hintergrundprozess, der auf der Grundlage einer Reihe von Regeln bestimmt, welche Elemente aus dem Cache entfernt werden sollten. Es gibt viele existierende Auslagerungsstrategien, die funktionieren können: LRU (Least Recently Used, d. h. das älteste Objekt), LFU (Least Frequently Used, d. h. 10 Treffer werden zugunsten von Objekten mit 100 Treffern verworfen) usw. Sie können nicht jedes Objekt, das hinzugefügt wird, für immer zwischenspeichern... Sie müssen einen Hintergrund-Thread hinzufügen, um die Auslagerung zu verwalten.

0 Stimmen

Aber dieser Hintergrund-Thread bereinigt den Cache nur ab und zu und irgendwann ist es zu spät.

7voto

supercat Punkte 72939

Es gibt eine Situation, in der ein WeakReference -basierten Cache gut sein kann: wenn die Nützlichkeit eines Elements in der Klasse von der Existenz eines Verweises darauf abhängt. In einer solchen Situation kann ein "weak interning"-Cache nützlich sein. Zum Beispiel, wenn man eine Anwendung hat, die viele große unveränderliche Objekte deserialisieren würde, von denen viele Duplikate sein dürften, und viele Vergleiche zwischen ihnen durchführen müsste. Wenn XY Verweise auf einen unveränderlichen Klassentyp sind, testen X.Equals(Y) ist sehr schnell, wenn beide Variablen auf dieselbe Instanz zeigen, kann aber sehr langsam sein, wenn sie auf verschiedene Instanzen zeigen, die zufällig gleich sind. Wenn ein deserialisiertes Objekt zufällig mit einem anderen Objekt übereinstimmt, auf das bereits ein Verweis existiert, kann das Abrufen eines Verweises auf das letztere Objekt aus dem Wörterbuch (was einen langsamen Vergleich erfordert) zukünftige Vergleiche beschleunigen. Andererseits, wenn es mit einem Objekt im Wörterbuch übereinstimmt, aber das Wörterbuch die nur Verweis auf dieses Element, gäbe es kaum einen Vorteil, das Wörterbuchobjekt zu verwenden, anstatt einfach das eingelesene Objekt zu behalten; wahrscheinlich nicht genug Vorteil, um die Kosten für den Vergleich zu rechtfertigen. Für einen internierenden Cache ist die Verwendung von WeakReferences so schnell wie möglich für ungültig erklärt werden, sobald keine anderen Verweise auf ein Objekt existieren, wäre eine gute Sache.

5voto

Nir Punkte 28685

In .net wird eine WeakReference aus Sicht der GC nicht als Referenz betrachtet, so dass jedes Objekt, das nur schwache Referenzen hat, im nächsten GC-Lauf (für die entsprechende Generation) gesammelt wird.

Das macht eine schwache Referenz für das Caching völlig ungeeignet - wie Ihre Erfahrung zeigt.

Sie brauchen eine "echte" Cache-Komponente, und das Wichtigste beim Caching ist, dass die Auslagerungsrichtlinien (d. h. die Regeln, wann ein Objekt aus dem Cache entfernt wird) gut zu den Nutzungsmustern Ihrer Anwendung passen.

3voto

Josh Punkte 66190

Nein, WeakReference ist dafür nicht geeignet, da sich das Verhalten des Garbage Collectors im Laufe der Zeit ändern kann und wird und Ihr Cache sich nicht auf das heutige Verhalten verlassen sollte. Außerdem können viele Faktoren, die außerhalb Ihrer Kontrolle liegen, den Speicherdruck beeinflussen.

Es gibt viele Implementierungen eines Cache für .NET. Sie könnten wahrscheinlich ein Dutzend auf CodePlex finden. Ich denke, was Sie brauchen, um es hinzuzufügen, ist etwas, das auf die aktuelle Arbeitsgruppe der Anwendung sieht, dass als Auslöser für die Bereinigung zu verwenden.

Noch eine Anmerkung dazu, warum Ihre Objekte so häufig eingesammelt werden. Die GC ist sehr aggressiv bei der Bereinigung von Gen0-Objekten. Wenn Ihre Objekte sehr kurzlebig sind (bis der einzige Verweis darauf ein schwacher Verweis ist), dann tut die GC, was sie tun soll, indem sie so schnell wie möglich aufräumt.

3 Stimmen

"ein Dutzend auf CodePlex" ... und eine im Framework. Der ASP.NET-Cache System.Web.Caching.Cache kann in Nicht-ASP.NET-Anwendungen verwendet werden und ist ziemlich leistungsfähig. Die Microsoft-Dokumentation sagt, dass es nicht empfohlen wird, ihn in Client-Anwendungen zu verwenden (sagt aber nicht wirklich warum), aber ich habe ihn in einer Vielzahl von Anwendungen erfolgreich eingesetzt.

1 Stimmen

Ich habe versucht, HttpRuntime.Cache, aber es funktioniert nicht wie ich möchte. Ich fügte dem Cache kontinuierlich Elemente hinzu und bekam bald eine OutOfMemoryException, anstatt dass der Cache Elemente evakuiert.

0 Stimmen

HttpRuntime.Cache", aber es funktioniert nicht so, wie ich möchte... OutOfMemoryException". Das ist überraschend, aber Sie könnten versuchen, die Cache-Konfiguration anzupassen - z. B. die Eigenschaften privateBytesLimit und percentagePhysicalMemoryUsedLimit. Oder es kann einen anderen Grund für die OutOfMemoryException geben.

2voto

Tim Lovell-Smith Punkte 14066

Ich glaube, das Problem, das Sie haben, ist, dass der Garbage Collector schwach referenzierte Objekte in der Antwort nicht entfernt nur als Reaktion auf den Speicherdruck - stattdessen wird die Sammlung manchmal recht aggressiv durchgeführt, nur weil das Laufzeitsystem der Meinung ist, dass einige Objekte wahrscheinlich unerreichbar geworden sind.

Möglicherweise ist es besser, z. B. System.Runtime.Caching.MemoryCache zu verwenden, der mit einem Speicherlimit oder benutzerdefinierten Auslagerungsrichtlinien für die Elemente konfiguriert werden kann.

CodeJaeger.com

CodeJaeger ist eine Gemeinschaft für Programmierer, die täglich Hilfe erhalten..
Wir haben viele Inhalte, und Sie können auch Ihre eigenen Fragen stellen oder die Fragen anderer Leute lösen.

Powered by:

X