JSP Tag Pooling Memory Leaks

Keine Kommentare

JSP Custom Tags waren früher verbreitet im Einsatz. Obwohl man sie heute nurnoch vereinzelnd schreibt, findet sich ein spezielles Problem in fast jedem Projekt in dem JSP Custom Tags eingesetzt werden.
Da in Produktion der eingesetzte Web Container die benutzten Tags poolen wird, muss man beim Schreiben von JSP Custom Tags den Lebenszyklus (lifecycle) berücksichtige. Das Tagpooling ist von der Spezifikation erlaubt und sogar empfohlen, verursacht aber Probleme wenn man daran nicht denkt. Speichert man große Objekte in Tags, verursacht man ein Memory Leak welches den Server abstürzen lässt (oder es passiert nichts schlimmes, für den Fall daß der Pool oder das Objekt klein genug ist). In der Entwicklungsumgebung fällt dies auch oft nicht auf.

Der Verursacher sieht meist so aus wie dieser Codeschnipsel:

public class MyTag extends TagSupport {
  public Object leak;
  public int doStartTag(){
    leak = new BigObject();
  }
}

Erzeugt wird das Problem dann durch diesen Lebenszyklus:

  1. Klasse laden
  2. Instanz erzeugen
  3. setPageContext() aufrufen
  4. Setter aufrufen
  5. doStartTag aufrufen
  6. In Abhängigkeit von Typ und Rückgabewerten andere Methoden aufrufen
  7. doEndTag() aufrufen
  8. Die Instanz in den Pool legen

Wenn nun der gleiche Tag wieder benutzt wird darf der Container bei Schritt 3 beginnen. Wenn nun der Tagpool auf 10 Objekte eingestellt ist und der gleiche Tag durch 10 simultane Anfragen erzeugt wird, landen 10 Instanzen im Pool. Folgen dann aber nur sporadisch Anfragen, dümpeln 10 Instanzen mit einer Referenz auf das große Objekt im Pool. Und schon haben wir unser Memory Leak.

Man kann dies aber ganz einfach vermeiden indem man „transiente“ Variablen am Ende auf null setze und sie in setPageContext() oder doStartTag() neu befüllt. Am Rande sei hier angemerkt, daß der Konstruktor eines Tags nur einmal gerufen werden könnte, obwohl der gleiche Tag auf hunderten von Seiten verwendet wird. Wie viele Instanzen erzeugt, und damit wie viele Konstruktoren aufgerufen, werden hängt von den Einstellungen des Containers, des Pools und der Serverlast ab.

public class MyTag extends TagSupport {
  public Object noLeak;
 
  public void setPageContext(PageContext pc){
    noLeak = new BigObject();
  }
 
  public int doStartTag(){
  }
 
  public void doEndTag(){
    noLeak = null;
  }
}

Weitere und bessere Alternativen wären diese Objekte so lokal wie möglich zu machen. Der Tag selbst sollte nur seine gesetzten Attribute kennen.
Ausser dem bisher beschriebenen Memoryleak entsteht noch ein weiteres Problem. Nehmen wir mal an ein Objekt wird nur gesetzt wenn ein bestimmtes Attribut einen bestimmten Wert hat. Ist dies der Fall, wird das Objekt aber nicht wieder entfernt, so findet man das Objekt bei der nächsten Benutzung des Tags wieder, obwohl die Bedingung auf das Attribut nicht mehr zutrifft.

Fabian Lange ist Lead Agent Engineer bei Instana und bei der codecentric als Performance Geek bekannt. Er baut leidenschaftlich gerne schnelle Software und hilft anderen dabei, das Gleiche zu tun.
Er kennt die Java Virtual Machine bis in die letzte Ecke und beherrscht verschiedenste Tools, die JVM, den JIT oder den GC zu verstehen.
Er ist beliebter Vortragender auf zahlreichen Konferenzen und wurde unter anderem mit dem JavaOne Rockstar Award ausgezeichnet.

Share on FacebookGoogle+Share on LinkedInTweet about this on TwitterShare on RedditDigg thisShare on StumbleUpon

Kommentieren

Deine E-Mail-Adresse wird nicht veröffentlicht. Erforderliche Felder sind mit * markiert.