WeakHashMap: Wie "null" effizient abfangen?

  • Themenstarter Themenstarter tuxedo
  • Beginndatum Beginndatum
Status
Nicht offen für weitere Antworten.
T

tuxedo

Gast
Hallo,

hab eine WeakHashMap die mir als eine Art Cache dient:

[HIGHLIGHT="Java"]private static WeakHashMap<Method, Long> methodHashs = new WeakHashMap<Method, Long>();[/HIGHLIGHT]

Ich benutze diese, um anhand einer Methode schnell den dazugehörigen "Hash" zu erhalten. Ist der Hash noch nicht in der Mapp enthalten, so berechne ich ihn und trage ihn in die Liste ein.

Bisher sieht die Methode so aus:

[HIGHLIGHT="Java"]public static long computeMethodHash(Method m) {

if (methodHashs.containsKey(m)) {

synchronized (methodHashs) {
logger.trace("Got hash from map. map contains {} entries.", methodHashs.size());
return methodHashs.get(m); // <--- Hier hagelts hin und wieder NPEs
}

} else {

// berechne hash ...
// ....
// ....

synchronized (methodHashs) {
methodHashs.put(m, hash);
logger.trace("computed new hash. map now contains {} entries.", methodHashs.size());
}

return hash;
}
}[/HIGHLIGHT]

In den meisten Fällen funktioniert der Code, da zwischen "containsKey()" und "get()" die Map nicht aufgeräumt wird. Aber hin und wieder hagelts da halt auch NullpointerExceptions, weil die Map sich zwischen den beiden Aufrufen schon geändert hat, sprich der GC den Eintrag unter umständen schon entsorgt hat.

Den Cache hab ich überhaupt erst eingeführt, weil das errechnen des Hashes etwas aufwendig ist und ich diesen Rechenaufwand gerne wo möglich vermeiden möchte. Es reicht ja wenn ich den Hash einmal berechne und ihn dann cache.

Aber zurück zum Problem. Da ich wie gesagt seehr oft den Hash zur Methode haben möchte (im Worst-Case etliche tausend mal pro Sekunde), und das möglichst effizient, weiß ich (noch) nicht wie man das am performantesten regelt.

Eine Idee wäre statt zu Fragen ob die Methode in der Map bekannt ist, den Hash einfach rausholen. Da primitive aber nicht "null" sein können, müsste ich das in ein Long Objekt oder so casten und schauen ob das null ist. Und erst wenn dieses NICHT null ist, den Wert des Long-Objekts zurückgeben. Ist das Long-Objekt null, so errechne ich den Hash.

Hab aber :rtfm: dass das casten, und vor allem die Nutzung von den Wrapper-Klassen für die primitiven Datentypen nicht so performant sein soll. Da ich aber für diesen Ansatz _jedesmal_ ein Wrqapper-Objekt bräuchte, wäre das wohl ein neuer Flaschenhals in meiner Anwendung :shock:

Hab auch versucht den ganzen Mehodenbody auf das Map-Objekt zu synchronisieren, aber geholfen hat's komischerweise nicht :autsch:

Letztendlich kann man die Frage wohl so formulieren:

Wie benutzt man eine WeakHashMap richtig und vor allem performant, so dass man zuverlässig keine NPE bekommt wenn man Elemente daraus haben möchte die eigtl. primitive Datentypen sind, und diese ggf. schon abgeräumt wurden ???:L

Gruß
Alex
 
Falsch Synchronisiert würd' ich sagen. Zwischen unsynchronisiertem "containsKey()" und synchronisiertem "get()" kann ein "remove()" aus einem anderen Thread dafür sorgen, das gerade dieser Eintrag entfernt wurde. Das das auf den gesammten Body nicht funktioniert hat könnte daran liegen, das die Map zu irgendeinem kritischen Zeitpunkt noch nicht initialisiert war.
 
Zuletzt bearbeitet von einem Moderator:
Wie wäre es mit einem komplett anderen Ansatz? Jedes Objekt sehe wie folgt aus:
[highlight="Java"]private boolean hashOK;
private int hash;

public int hashCode(){
if(!hashOK){
hash=/* Hash berechnen */;
hashOK=true;
}
return hash;
}[/highlight]
Das Flag hashOK muss natürlich immer zurückgesetzt werden, wenn sich ein Feld, das in hashCode() benutzt wird, ändert.

Über eine passende abstrakte Superklasse lässt sich der Code vielleicht auch sauberer gestalten.

Ark

EDIT: Ich überlege gerade, ob dies in diesem Fall überhaupt möglich ist ...

BTW: Die Mehrzahl von hash ist hashes.
 
Zuletzt bearbeitet:
Eine Idee wäre statt zu Fragen ob die Methode in der Map bekannt ist, den Hash einfach rausholen. Da primitive aber nicht "null" sein können, müsste ich das in ein Long Objekt oder so casten und schauen ob das null ist. Und erst wenn dieses NICHT null ist, den Wert des Long-Objekts zurückgeben. Ist das Long-Objekt null, so errechne ich den Hash.
Das wäre die Lösung.
Hab aber :rtfm: dass das casten, und vor allem die Nutzung von den Wrapper-Klassen für die primitiven Datentypen nicht so performant sein soll. Da ich aber für diesen Ansatz _jedesmal_ ein Wrqapper-Objekt bräuchte, wäre das wohl ein neuer Flaschenhals in meiner Anwendung :shock:
Möchtest du ein super-performantes Programm, was nicht funktioniert? Oder lieber ein funktionierendes Programm, was möglicherweise ein paar Prozent langsamer ist?
 
Hab aber :rtfm: dass das casten, und vor allem die Nutzung von den Wrapper-Klassen für die primitiven Datentypen nicht so performant sein soll. Da ich aber für diesen Ansatz _jedesmal_ ein Wrqapper-Objekt bräuchte, wäre das wohl ein neuer Flaschenhals in meiner Anwendung :shock:
Was denkst du denn was in deiner Map drin liegt? long sicherlich nicht, da ein Object erwartet wird 😉
 
Falsch Synchronisiert würd' ich sagen. Zwischen unsynchronisiertem "containsKey()" und synchronisiertem "get()" kann ein "remove()" aus einem anderen Thread dafür sorgen, das gerade dieser Eintrag entfernt wurde. Das das auf den gesammten Body nicht funktioniert hat könnte daran liegen, das die Map zu irgendeinem kritischen Zeitpunkt noch nicht initialisiert war.

Das mein erster Ansatz nicht funktioniert weiß ich auch, hatte ich ja geschrieben.

Das mit dem ganzen Body synchronisieren: Für "Fremzzugriffe" mag das funktionieren. Aber das ist eine Weak-HashMap. D.h. da "pfuscht" der GC mit rein. Und ich hab kein Plan ob der sich am "synchronized" stört. So wie's aussieht nicht.

Initialisiert ist die Map zum Zeitpunkt im dem die Klasse, die die Map als Member hat, vom Classloader geladen wird:

[HIGHLIGHT="Java"] private static WeakHashMap<Method, Long> methodHashs = new WeakHashMap<Method, Long>();[/HIGHLIGHT]

@Ark

Es geht nicht drum die korrektheit des Hashes zu testen. Ich nutze den Hash als "komnpakte" ID für die Methode über Rechnergrenzen hinweg.

@tfa

Das das funktioniert ist mir klar. Was ich eigentlich wissen wollte ist, ob es einen besseren, wohlmöglich performanteren Weg gibt.

@Wildcard

Ich denke das hat mir die Augen geöffnet. Denn schließlich muss ich die Map ja so definieren:

private static WeakHashMap<Method, Long> methodHashs = new WeakHashMap<Method, Long>();

Hätte ich auch früher drauf kommen können 😱

Problem ist also gelöst. Machmal sieht man tatsächlich den Wald vor lauter Bäumen nicht mehr.

- Alex
 
Status
Nicht offen für weitere Antworten.

Neue Themen


Zurück
Oben