# Probleme mit java.util.HashMap als Cache



## Marine_of_Hell (31. August 2010)

Hallo Zusammen!

Ich habe ein Problem mit einer selbstgebastelten Cache-Klasse. Anscheinend werden die Objekte innerhalb einer HashMap durch den GC zur Laufzeit gelöscht und ich habe stellenweise null Werte. Das führt zu unkontrollierten NullPointerExceptions.

Allerdings ist mir nicht ganz klar, wieso der GC die Objekte löscht. Die HashMap selbst ist mit den Parametern String und CacheObjectWrapper<?> initialisiert.
CacheObjectWrapper ist eine Generic-Klasse die wieder rum verschiedene Objekte beinhaltet. Das wurde so gelöst um TypeErasure's zu vermeiden.

Die Klasse Cache selbst, ist eine Singleton-Klasse und soll innerhalb der Tomcat-Anwendung (sessionübergreifend) arbeiten.

Ich weiss nicht ob es Sinn macht jetzt hier meine Klassen zu posten - ich lasse es vorerst mal um den Überblick zu waren.

Jemand eine Idee weshalb ich null-Werte in meiner HashMap habe?
Ich bin für jeden Tipp dankbar.

Gruß,
Thomas


----------



## Franz Degenhardt (31. August 2010)

Hallo, 

wenn es meine Klasse wäre, würde ich als erstes einen bug meinerseits vermuten. Etwas wie, davon auszugehen, dass an anderer Stelle etwas in die map gestellt wird, wo durch eine unvermutete Konstellation in einigen Fällen nichts eingestellt wurde. 

Davon abgesehen ist eine HashMap nicht synchronisiert. Mehrere Anwendungen, welche gleichzeitig mit der HashMap arbeiten könnten mit zeitlich versetzt Daten arbeiten. Dies wird durch das Singletonpattern nicht gelöst. Die manuelle Synchronisation über die verwendeten Methoden kann dann abhilfe schaffen. 

Woher genau stammt deine Klasse CacheObjectWrapper? Bzw. da ein Cache auch eine Expiry-Funktionalität benötigt, in der nicht mehr benötigte Objekte aufgeräumt werden, wäre es natürlich wichtig zu wissen, wie diese Klasse genau arbeitet.

Grüsse


----------



## Marine_of_Hell (31. August 2010)

Hallo!

Danke schon mal für deine Antwort.
Also wenn ich deinen ersten Abschnitt richtig interpretiere, siehst du es als unüblich an, dass der GC die Objekte rausschmeisst!?

Ich habe es auch schon mit einer synchronized HashMap probiert - allerdings nicht mit manueller Synchronisation der Methoden. Die Klasse CacheObjectWrapper hingegen ist ebenfalls eine selbstgebaute generische Klasse.
Sie beinhaltet auch keine Expiry-Funktionalität, da hier Objekte gespeichert werden die kein Verfallsdatum haben sollen (z.B. Länderlisten, Sprach-Objekte etc.). Diese sollen nur beim Tomcat-Start einmal geholt werden (via WebServices) und anschließend im Cache gehalten werden.

Gruß,
Thomas


----------



## Franz Degenhardt (31. August 2010)

Verstehe. Genau, ich habe noch nicht erlebt, dass mir der GC etwas aufräumt, was ich noch brauche.

Da du den Cache ja initial befüllst und dann nicht mehr grossartig veränderst, würde ich Synchronisation fast ausschliessen. Solche Probleme ergeben sich eher, wenn eine Servletinstanz dort etwas reinschreibt und recht zeitnah (sekundenbereich) eine andere Instanz genau dies auslesen will. Dies ist ja nicht der Fall, wenn ich das richtig verstanden habe.

Ohne einen Expiry-Mechanismus, der einem etwas weglöschen könnte, fällt dies auch weg.

Die korrekte intiale Befüllung hast du überprüft? Also nach der Initialisierung enthält der Cache alle erwarteten Daten?

Sonst habe ich dort auch gerade keine Idee.

edit: Was genau ist null? ist der Schlüssel nicht vorhanden? Oder der Value dazu null? Die Klasse CacheObjectWrapper könntest du einfach mal posten.


----------



## Marine_of_Hell (31. August 2010)

Ich prüfe das noch einmal ggf. wird da zwischenzeitlich was überschrieben.
Ansonsten was ich auch für sehr seltsam halte ist, das meine Instanz vom Cache ab und an mal auch null wird. Ich benutze die zwei-drei Zeilen vorher und dann auf einmal ist die Instanz (protected static) null.
Dabei prüfe ich das vor der Abfrage des Caches immer und wenn dies der Fall ist hole ich mir eine neue Instanz (getInstance) und reinitialisiere den Cache.

Hier mal ein Beispiel:

```
if(cache == null)
	cache = Cache.getInstance();

try {
	if(!cache.containsCacheItem("wsconfig"))
		cache.setCacheItem("wsconfig",
			new CacheObjectWrapper<WebServiceConfig>(wsConfig));
} catch ( CacheException e ) {
				throw new WebServiceException(e);
}
```

WebServiceConfig ist dabei eine Klasse die Konfigurationen für die WebServices beinhaltet.
Die NullPointerException tritt in der Zeile "cache.setCacheItem("wsconfig"," auf.

Gruß,
Thomas


----------



## Thomas Darimont (31. August 2010)

Hallo,

ist der Code bei dir genauso formatiert wie im Beispiel? Wenn ja, dann würde das bedeuten, dass cache null ist... wobei er dann aber schon mit einer NPE ausgestiegen wäre wenn if(!cache....) geprüft wird. Wird cache noch an anderer Stelle initialisiert oder auf zurückgesetzt null gesetzt?

Zeig doch einfach mal den genauen Stacktrace. 

Gruß Tom


----------



## Marine_of_Hell (31. August 2010)

Hi Tom!

Danke für deine Antwort. Allerdings kann ich Dir nicht ganz folgen. Wieso soll da cache null gesetzt werden?

Leider habe ich aktuell den StackTrace nicht zur Hand - er steigt aber auch an anderen Stellen raus. Generell prüfe ich aber wie oben im Beispiel gezeigt ob cache null ist und hole mir ggf. eine neue Instanz. Was spricht dagegen? 

Gruß,
Thomas


----------



## Thomas Darimont (31. August 2010)

Hallo,

wenn die eine NPE an einer Stelle wie:
foo.op(....)
erhälst, dann war foo null (es wurde quasi versucht null.op(....) aufzurufen ) (Es sei denn die Auswertung eines Arguments von op(...) hat eine NPE hervorgebracht...)

Die von dir zitierte Stelle hatte folgendes Muster:

if(foo.otherOp()){
   foo.op(....)
}

Der if check geht durch es wird der innere Block der Bedingung betreten. D.h. foo war an der Stelle nicht null. Beim Aufruf von foo.op(...) gibts dann eine NPE mit der angegebenen Stelle im Stacktrace.

Da ich annehme, dass bei dir die Auswertung der Argumente nicht zu einer NPE führen (kenne die Implementierung von deinem CacheObjectWrapper nicht) bleibt nur noch die Variable cache übrig. Da diese im vorhergehenden Statement nicht null war (wenn diese null gewesen wäre hattest du schon beim if(...) eine NPE bekommen) muss cache von der einen Zeile zur anderen null werden. Diese Art von Fehler ist charakteristisch nebenläufige Zugriffe... (ein anderer Thread setzt cache auf null...?!)

Deshalb bräuchten wir ein wenig mehr Code und den Stacktrace um das abklären zu können.

Gruß Tom


----------



## Marine_of_Hell (1. September 2010)

Moin Tom!

Ich denke da ist ein kleines Missverständnis wg. der NPE!

```
if(cache == null)
    cache = Cache.getInstance();
```

Kann keine NPE erzeugen - da die if-Prüfung ja auf null geht und die Variable cache mit eine Instanz der Klasse Cache (man beachte Groß-/Kleinschreibung) sich holt. Es findet ja an dieser Stelle keinen Zugriff auf die Variable cache mit dem Wert null statt, sie erhält eine neue Instanz.

Wie dem auch sei - manchmal klappt es am nächsten Morgen einfach besser und siehe da mein Problem war ein "völlig anderes". Ich habe in den Klassen - die den Cache nutzen - die finalizer-Methode überschrieben und dort mir meinen Cache gelöscht/geleert. Das ist natürlich sau-dumm!
Wie dem auch sei - die Zeilen rausgeschmissen und siehe da - keine null-Werte mehr! 

Ihr hattet also Recht - ich habe die Werte selbst wieder überschrieben/gelöscht.
An dieser Stelle noch einmal vielen Dank für eure Hilfen und Mühen.

Gruß,
Thomas


----------

