JLabel Klasse, die als eigenen Text die Uhrzeit setzt und aktualisiert - Memory Leak?

DarthShader

Erfahrenes Mitglied
Hallo zusammen,

ich habe eine einfache, kleine Klasse entwickelt, die von JLabel erbt und die aktuelle Uhrzeit anzeigt. Durch einen ExecutorService wird die aktuelle Uhrzeit abgefragt und dann via "setText" des Labels gesetzt.

Die Klasse sieht so aus:

Java:
public class TimeLabel extends JLabel
{
  private static final int            INITIAL_UPDATE_DELAY_MS     = 2000;
  private static final int            UPDATE_PERIOD_MS            = 5000;
    
  private ScheduledExecutorService    scheduledExecutorService    = Executors.newSingleThreadScheduledExecutor();

  public TimeLabel()
  {
    updateLabelOnEventDispatchThread();

    scheduledExecutorService.scheduleAtFixedRate( new LabelUpdateTask(), 
        INITIAL_UPDATE_DELAY_MS, UPDATE_PERIOD_MS, TimeUnit.MILLISECONDS );
  }

  private void updateLabelOnEventDispatchThread()
  {
    SwingUtilities.invokeLater( new Runnable() {
      @Override public void run() {
            
        // TimeUtil ist eine Klasse, die via .format den übergebenen Calendar als String formatiert
        setText( TimeUtil.format( Calendar.getInstance(), "dd.MM.yyyy HH:mm" ) );
      }
    } );
  }
    
  private class LabelUpdateTask implements Runnable
  {
    @Override
    public void run()
    {
      updateLabelOnEventDispatchThread();
    }
  } 
}

Meine simple Frage ist nun - wird durch diesen Code ein Memory Leak erzeugt?

Ich habe folgendes Szenario im Kopf:

  1. Man fügt dieses Label einem JFrame hinzu
  2. Während das Programm läuft, wird der JFrame irgendwann mal geschlossen und für den Garbage Collector freigegeben
  3. Dieser räumt den JFrame, und damit auch alle Komponenten des JFrames weg
  4. Eigentlich sollte damit auch das TimeLabel vom GC gelöscht werden. Aber dies passiert nicht, weil der ExecutorService ja noch läuft, auf ihm wurde noch kein "shutdown" aufgerufen

Ist diese Überlegung korrekt, birgt die Klasse oben also dieses Problem?

Falls ja, wie könnte man es richtig machen?

(Die einfache Lösung, von außen beim Schließen des Parents dann "shutdown" auf dem ExecutorService des Labels aufzurufen, reicht mir nicht. Den Grad an Automatismus würde ich natürlich schon gerne behalten.)


Über Eure Hilfe würde ich mich sehr freuen


Vielen Dank!
 
Zuletzt bearbeitet:
Hallo,

du könntest in deiner updateLabelOnEventDispatchThread()-Methode abfragen, ob das JLabel noch sichtbar ist (.isvisible()). Falls das nicht der Fall ist wird ein .shutdown gemacht. (Allerdings mußt du noch die Startbedingung bearbeiten, denn wenn du dein JLabel zum JFrame hinzufügst, ist das JFrame ja noch nicht sichtbar).

Allerdings glaube ich, daß das nicht nötig ist, den mit einem .dispose() auf das JFrame sollten (lt. Api) ja alle Resourcen wieder freigegeben werden.

Zum Testen brauchst du ja eigendlich nur ein Sysout vor oder nach deinem .setText() machen. Dann siehts du schon nach dem Schießen deines Fensters, ob der ExecutorService noch läuft oder vom GC beendet wird.

MfG
hansmueller
 
Hallo hansmueller,

danke für Deine Antwort.

du könntest in deiner updateLabelOnEventDispatchThread()-Methode abfragen, ob das JLabel noch sichtbar ist (.isvisible()). Falls das nicht der Fall ist wird ein .shutdown gemacht. (Allerdings mußt du noch die Startbedingung bearbeiten, denn wenn du dein JLabel zum JFrame hinzufügst, ist das JFrame ja noch nicht sichtbar).

Diesen Ansatz finde ich zu instabil. Genau aus dem Grund, den Du schon genannt hast - denn ein isVisible() == false ist kein Beweis dafür, dass das Label nicht mehr gebraucht wird. Nicht nur am Start ist das schwierig, sondern immer: was, wenn ich das Label mal aus Intension ausblende, es auf ein anderes Frame schiebe oder sonstwas - es gibt viele Fälle, wo es unsichtbar sein könnte.

Allerdings glaube ich, daß das nicht nötig ist, den mit einem .dispose() auf das JFrame sollten (lt. Api) ja alle Resourcen wieder freigegeben werden.

Hm, was heißt denn "Ressourcen" in einem solchen Fall? Ich dachte das wären immer die nativen Fenster, also die "Peers" die von Java/AWT erstellt werden.

Wäre mal interessant zu wissen, ob auch ein ExecutorService, der in dem Label erstellt wird, dazu gehört (was ist z.B., wenn das Label zwar als Child in einem JFrame sitzt, ich aber noch eine andere Referenz auf das Label habe, die verhindert, dass das Label weggeräumt wird; dann würde das Label nicht mehr "funktionieren" (da der ExecutorService dann gestoppt wird), obwohl es aber noch funktionieren sollte).

Mein gedanklicher Ansatz war noch, dass ich einen eigenen ExecutorService schreibe, der eine WeakReference auf das Label hat und periodisch prüft, ob die Referenz mit .get() "null" zurück gibt. Ist dem so, wurde das Label weggeräumt, und der ExecutorService kann quasi auf sich selbst .shutdown() aufrufen. Das muss ich aber noch testen.
 
Zurück