2010-01-28 3 views
24

Ich muss Objekte in Java mit einem Anteil von was RAM verfügbar ist, zwischenspeichern. Mir ist bewusst, dass andere diese Frage gestellt haben, aber keine der Antworten entspricht meinen Anforderungen.Wie kann ich Objekte in Java mit dem verfügbaren RAM effizient zwischenspeichern?

Meine Anforderungen sind:

  • Einfache und leichte
  • nicht dramatisch langsamer als eine einfache HashMap
  • Verwendung LRU, oder eine Löschrichtlinie, die LRU

I LinkedHashMap versuchte annähert, Allerdings müssen Sie eine maximale Anzahl von Elementen angeben, und ich weiß nicht, wie viele Elemente benötigt werden, um den verfügbaren Arbeitsspeicher zu füllen (ihre Größe variiert erheblich).

Mein aktueller Ansatz ist Google Collection MapMaker wie folgt zu verwenden:

Map<String, Object> cache = new MapMaker().softKeys().makeMap(); 

Dies schien attraktiv, da sie automatisch Elemente löschen sollen, wenn es mehr RAM benötigt, aber es ist ein ernstes Problem: sein Verhalten zu füllen den gesamten verfügbaren Arbeitsspeicher auf, woraufhin der GC zu thrashen beginnt und sich die Leistung der ganzen App dramatisch verschlechtert.

Ich habe von Sachen wie EHCache gehört, aber es scheint ziemlich schwer für das, was ich brauche, und ich bin mir nicht sicher, ob es schnell genug für meine Anwendung ist (bedenkt, dass die Lösung nicht dramatisch langsamer sein kann) als eine HashMap).

+0

welche Art von Objekten sind Sie Caching? Ich bin nicht ganz, warum Sie sich Sorgen über die Leistung des Caches machen, wenn Sie nach einer Ablaufrichtlinie denken, werden Sie mehr Overhead als eine einfache Karte und EHCache ist eine gut entwickelte Caching-Lib, die (I Ich denke hier über Spring konfiguriert) ist nicht kompliziert einzurichten und ebenso einfach zu bedienen wie eine Karte. – beny23

+0

Die Objekte variieren in der Größe von etwa 1kb bis vielleicht 10kbs. Ich mache mir Sorgen um die Leistung, weil das Abrufen von Objekten aus dem Cache in der inneren Schleife eines sehr rechenintensiven Prozesses stattfindet. Wenn es langsam ist, kann es die Zeit, die für meine App benötigt wird, um von Minuten zu Stunden zu gehen, erhöhen. – sanity

+0

Mit SoftKeys() erhalten Sie keinen Treffer, wenn Sie equals() verwenden, Sie erhalten nur einen Treffer, wenn Sie das Objekt mit Referenzgleichheit nachschlagen. Wenn Sie equals() für Cache-Treffer benötigen, verwenden Sie stattdessen softValues ​​(). –

Antwort

7

ich ähnliche Anforderungen an die Sie haben - Concurrency (2 hexacore CPUs) und LRU oder ähnlich - und versuchte auch Guava MapMaker. Ich fand softValues ​​() viel langsamer als weakValues ​​(), aber beide machten meine App quälend langsam, wenn der Speicher voll wurde.

Ich versuchte WeakHashMap und es war weniger problematisch, seltsam sogar noch schneller als LinkedHashMap als LRU-Cache über seine removeEldestEntry() -Methode zu verwenden.

Aber am schnellsten für mich ist ConcurrentLinkedHashMap, die meine App 3-4 (!!) mal schneller gemacht hat als jeder andere Cache, den ich ausprobiert habe. Freude, nach Tagen der Frustration! Es wurde anscheinend in Guava MapMaker integriert, aber die LRU-Funktion ist nicht in Guava R07 auf jeden Fall. Hoffe es funktioniert für dich.

0

Dies schien attraktiv wie es sollte automatisch Elemente löschen, wenn es mehr RAM benötigt, aber es gibt ein ernstes Problem ist: sein Verhalten zu ist, alle verfügbaren RAM

über Softkeys füllen erlaubt dem Garbage Collector nur, Objekte aus dem Cache zu entfernen, wenn keine anderen Objekte auf sie verweisen (dh wenn der Cacheschlüssel sich nur auf den Cache selbst bezieht). Es garantiert keine andere Art von Ausweisung.

Die meisten Lösungen, die Sie finden, sind Funktionen, die zusätzlich zu den Java Map-Klassen hinzugefügt werden, einschließlich EhCache.

Haben Sie sich die commons-collections LRUMap angesehen?

Beachten Sie, dass es für MapMaker eine open issue gibt, die LRU/MRU-Funktionalität bietet. Vielleicht können Sie auch dort Ihre Meinung äußern

+0

LRUMap muss angegeben werden die maximale Größe der Map, aber ich weiß nicht, was das sein wird - ich möchte nur, dass der Cache wächst, um ansonsten ungenutzten RAM zu füllen. – sanity

0

Verwenden Sie Ihren vorhandenen Cache, speichern Sie WeakReference lieber als normale Objektreferenzen.

Wenn der freie Speicherplatz von GC erschöpft ist, werden die Werte von WeakReferences freigegeben.

+0

würde das ungefähr LRU obwohl? – beny23

+0

Sie denken an 'SoftReference' - schwach referenzierte Objekte können jederzeit ohne Rücksicht auf freien Speicher zurückgewonnen werden. – danben

+0

Siehe diesen Vortrag über die Unterschiede zwischen schwachen und weichen Referenzen: http://crazybobs-talks.googlecode.com/svn/trunk/out/references.pdf und http://www.parleys.com/#st=5&id= 2657 & sl = 53. –

-3

Angenommen, Sie möchten, dass der Cache Thread-sicher ist, dann sollten Sie das Cache-Beispiel in Brian Goetz 'Buch "Java Concurrency in Practice" untersuchen. Ich kann das nicht genug empfehlen.

+0

Das hat seine Frage überhaupt nicht angesprochen. – danben

+0

@danben: Ich stimme zu. Aber Thread-Sicherheit ist etwas, das er berücksichtigen muss. –

4

Ich habe Serval Caches implementiert und es ist wahrscheinlich so schwierig wie die Implementierung einer neuen Datenquelle oder Threadpool, meine Empfehlung ist jboss-cache oder eine andere bekannte Caching-lib. So schlafen Sie gut ohne Probleme

0

In der Vergangenheit habe ich JCS verwendet. Sie können das configuration einrichten, um Ihre Bedürfnisse zu erfüllen. Ich bin mir nicht sicher, ob dies alle Ihre Anforderungen/Bedürfnisse erfüllen wird, aber ich fand es ziemlich mächtig, wenn ich es benutze.

0

Sie können nicht „löschen Elemente“ Sie können nur schwer verhindern, dass sie für die GC verweisen und warten, um sie zu reinigen, so gehen mit Google Kollektionen ...

0

Ich bin mir nicht bewusst eine einfache Möglichkeit, finde die Größe eines Objekts in Java heraus. Daher glaube ich nicht, dass Sie eine Möglichkeit finden, eine Datenstruktur durch die Menge an RAM zu begrenzen, die sie benötigt.

Aufgrund dieser Annahme müssen Sie die Anzahl der zwischengespeicherten Objekte begrenzen. Ich würde vorschlagen, Simulationen einiger praktischer Nutzungsszenarien auszuführen und Statistiken über die Arten von Objekten zu sammeln, die in den Cache gelangen. Dann können Sie die statistisch gemittelte Größe und die Anzahl der Objekte berechnen, die Sie sich leisten können. Auch wenn es nur eine Annäherung an die Größe des Arbeitsspeichers ist, den Sie dem Cache zuweisen möchten, könnte es gut genug sein.

Was die Cache-Implementierung angeht, verwenden wir in meinem Projekt (einer performancekritischen Anwendung) EhCache, und persönlich finde ich es nicht als Schwergewicht.

Führen Sie in jedem Fall mehrere Tests mit verschiedenen Konfigurationen durch (hinsichtlich Größe, Räumungsrichtlinien usw.) und finden Sie heraus, was für Sie am besten funktioniert.

3

Ich habe von Sachen wie EHCache gehört, aber es scheint ziemlich schwer für das, was ich brauche, und ich bin mir nicht sicher, ob es schnell genug für meine Anwendung ist (daran erinnern, dass die Lösung nicht sein kann) dramatisch langsamer als eine HashMap).

Ich weiß wirklich nicht, ob man sagen kann, dass EHCache schwer ist. Zumindest betrachte ich EHCache nicht als solches, besonders wenn man eine Memory Store verwendet (die von einem erweiterten LinkedHashMap unterstützt wird und natürlich die schnellste Caching-Option ist). Du solltest es versuchen.

+0

Diese Lösung teilt das gleiche Problem, das ich mit LinkedHashMap erwähnt habe, nämlich (aus den EHCache-Dokumenten) "Alle Caches geben ihre maximale speicherinterne Größe in Bezug auf die Anzahl der Elemente zur Konfigurationszeit an." – sanity

+0

@sanity Ich sehe ... aber wenn Sie nicht wollen * alle * den verfügbaren Speicher mit Ihren Objekten im Cache (und damit GC zu vermeiden), dann sehe ich nicht, wie dies zu tun, ohne die Anzahl der Objekte zu begrenzen im Cache. Haben Sie repräsentative Benchmarks durchgeführt, um eine Vorstellung von der Speichergröße für verschiedene Limits zu bekommen? –

+0

Ich werde wahrscheinlich am Ende ein Benchmarking machen, wenn sich keine bessere Option ergibt, aber es macht mir Sorgen, weil die Realität in unerwarteter Weise von meinen Benchmarks abweichen kann und alle Arten von Kopfschmerzen verursacht. – sanity

1

Ich weiß nicht, ob dies eine einfache Lösung wäre, besonders im Vergleich zu EHCache oder ähnliches, aber haben Sie sich die Javolution library angesehen? Es ist nicht als solches konzipiert, aber im Paket javolution.context haben sie ein Allocator-Muster, das Objekte ohne die Notwendigkeit einer Garbage-Collection wiederverwenden kann. Auf diese Weise minimieren sie die Erstellung von Objekten und die Speicherbereinigung auf ein Minimum, eine wichtige Funktion für die Echtzeitprogrammierung. Vielleicht sollten Sie einen Blick darauf werfen und versuchen, es an Ihr Problem anzupassen.

2

Ich glaube, MapMaker wird der einzige vernünftige Weg, um zu bekommen, was Sie verlangen. Wenn "der GC anfängt zu schlagen und sich die Performance der gesamten App dramatisch verschlechtert", sollten Sie einige Zeit damit verbringen, die verschiedenen Tuning-Parameter richtig einzustellen. Dieses Dokument kann ein wenig scheinen auf den ersten einschüchternd, aber es ist eigentlich sehr klar geschrieben und ist eine Fundgrube von hilfreichen Informationen über GC:

http://java.sun.com/j2se/reference/whitepapers/memorymanagement_whitepaper.pdf

+0

Ich kenne dieses Dokument, aber mir ist nichts bekannt, das mein Problem löst :-( – sanity

0

Caching etwas, SoftReference vielleicht der beste Weg, bis jetzt kann ich mir vorstellen.

Oder Sie können einen Object-Pool neu erfinden. Jedes Objekt, das du nicht benutzt, musst du nicht zerstören. Aber es, um CPU zu sparen, anstatt Speicher zu sparen

Verwandte Themen