WeakReference und GC

Status
Nicht offen für weitere Antworten.
G

Guest

Gast
Hi,

kennt jemand die Lösung für folgendes Problem:

1) Es liegt eine Liste (ArrayList) mit WeakReference(n) vor, die auf eine
Reihe von Listener-Objekten verweisen.
Es sind u.a. auch JInternalFrame, die das ListenerInterface implementieren.
Grundsätzlich gibt es zwei Typen dieser Listener.

a) direkt in den Frames implementiert

class Whatever extends JInternalFrame implements MyListener
{
...
}

b) als Adapter mit Delegation an das Frame

class Whatever extends JInternalFrame
{
MyListener listener = new MyAdapter();
...
}

2) Wird ein Fenster geschlossen/entfernt (DISPOSE_ON_CLOSE), dauert es
immer viel zu lange, bis sie vom GC als "weakly reachable" markiert werden.

3) Expliziter Aufruf von System.gc() führt auch nicht dazu, dass die
WeakReference-Instanzen "null" bei der Methode get() zurückgeben.
Klartext: Es existieren keine "harten" Referenzen mehr, die auf die Listener
Objekte verweisen. (auch nicht in JDesktopPane oder sonstwo)

4) Erst nach einer gewissen Zeit (>1 Minute), sind die Listener wirklich
weg bzw. WeakReference.get() gibt erst dann null zurück.

Folgender Codeausschnitt zeigt, wie es funktionieren sollte:
Code:
    // Queue, in die nicht mehr "gültige" WeakReferenzen kommen
    ReferenceQueue refQueue = new ReferenceQueue();

    // Eine Referenz für den Test
    Integer intField = new Integer(1);
    // Und das dazugehörige WeakReference 
    WeakReference ref = new WeakReference(intField, refQueue);

    // Die Referenz ist noch gülig, daher ist die Ausgabe "false" OK
    System.out.println("ref.get()==null  : "+ (ref.get()==null));

    // Die Referenz wird entfernt und GC aufgelöst
    intField = null; 
    System.gc();

    // Hier ist die Referenz nicht mehr gültig; "true" ist also OK
    System.out.println("ref.get()==null  : "+ (ref.get()==null));

    WeakReference tmpRef;
    // Wenn das nicht mehr referenzierte Objekt in die Queue gesetzt wurde
    if((tmpRef = (WeakReference)refQueue.poll()) != null)
    {
      // Alles OK, das Ding ist endgültig weg
      System.out.println("ref enqueued     : " + (tmpRef.get()==null));  
    }
    else
    {
      // Wurde nicht freigegeben.
      System.out.println("ref NOT enqueued");
    }

Das Problem sind die "Listener-Leichen", die in einem undefinierten
Zustand sind. Es kommt zu unvorhersehbaren Fehlern, wenn man
Events an diese Listener schickt.

Eine Idee was die Ursache sein kann?

Gruß,
Michael
 
G

Guest

Gast
Weiss keiner eine Lösung für das obige Problem, ausser die Listener extra
zu entfernen? (addFooListener(...), dann bei bedarf removeFooListener(...))

Ich wollte mir mit den WeakReferences die Probleme mit nicht abgemeldeten
Listenern vom Hals schaffen, da fliegt mir jetzt von Zeit zu Zeit der Client um
die Ohren, da irgendwelche Zombies im Speicher immer noch auf Events
reagieren. Taugen WeakReferences nur für "readonly" Daten, oder wie?

Help!
 
B

Beni

Gast
Das Problem ist wohl, dass hier wirklich keiner eine bessere Lösung als
Weiss keiner eine Lösung für das obige Problem, ausser die Listener extra
zu entfernen? (addFooListener(...), dann bei bedarf removeFooListener(...))
anbieten kann.
 
G

Guest

Gast
Klar. Dies ist der 'normale' Weg. :)
Es geht mir jetzt eigentlich nur um die WeakReference-Objekte.
Wozu taugen sie, wenn sie trotz fehlender Referenzen auf ein Objekt
immer noch nicht 'null' liefern. Nach meinem Verständnis, sollten diese
'null' zurückgeben, sobald ein Objekt nicht mehr referenziert wird.
Bei SoftReference ist das Verhalten klar. Sie werden als erste 'geopfert',
wenn Speicher knapp wird (gut für MRU-Ressourcen; Caching etc.).
PhantomReference ist für meine Zwecke auch nicht geeignet, da diese
erst nach dem endgültigen Entfernen eines Objektes aus dem Heap
vom GC in die ReferenceQueue gesetzt werden.

Noch mal kurz zur Beschreibung, was das Problem ist.

- Ein InternalFrame mit komplexen Inhalt wird komplett entfernt.
*- Beim Schliessen, gibt es seine Ressourcen frei.
- Es ist, von aussen, an einer Art MessageBus registriert, wo es
auf diverse Messages/Events anderer Container etc. reagiert.
- Hat das InternalFrame seine Ressourcen freigegeben, dann ist es
in einem nicht definierten Zustand.
- Wird dann irgendein Event ausgelöst, auf das das Frame normalerweise reagiert,
dann löst dies innerhalb des Frames bestimmte Aktionen aus.
- Da es aber seine Ressourcen freigegeben hat, kommt es dabei zu
Fehlern. (bis hin zu NullPointerException etc.)

*OK, normalerweise könnte sich das Frame beim Schliessen aus dem MessageBus
ausklinken. Das Problem ist, dass die Entscheidung darüber, an welchen
MessageBus es angeschlossen wird, von Aussen gesteuert wird.
Klartext: Das Frame ist in dieser Hinsicht strohdoff. Es lauscht auf Events,
ohne deren Quelle kennen zu müssen. Es implementiert nur ein bestimmtes
Listener-Interface, und bekommt nur Events, die es versteht bzw. für die es
registriert wurde.
Alle anderen Objekte, wie z.B. diverse Action-Objekte, die ebenfalls an dem
MessageBus dranhängen, werden über die WeakReferences korrekt
entfernt. Das ganze ist ähnlich aufgebaut wie InfoBus.

Ich war schon einen ganzen Tag nur am Debuggen, und bin mir sicher, dass
die Frames garaniert nirgendwo referenziert werden.
Sie sind weg, nur der GC hat's nicht mitgekriegt. :?

Gruß,
Michael
 
B

Beni

Gast
Ich bin nicht ganz sicher, ob ich verstehe ???:L

Aber solange es Events bekommen kann, muss noch jemand eine Referenc auf das Frame haben. Denn ansonsten wäre es gar nicht möglich, dass jemand zum Frame kommt.
 
G

Guest

Gast
Die Listener werden als WeakReference-Objekte verwaltet.

Es sieht ungefähr so aus (alles synchron und threadsafe)
Code:
...

public void addFooListener(FooListener listener, ....)
{
  ...
  synchronized (this.listenerList)
  {
    this.listenerList.add([b]new WeakReference(listener, referenceQueue)[/b]);
  }
  ...
}

public synchronized void sendMessage(...)
{
  [b]// Alle ungültigen Referenzen entfernen[/b]
  WeakReference ref;   
  while((ref = (WeakReference)referenceQueue.poll()) != null)
  {
    this.listenerList.remove(ref);
  }

  // und die Message/Event synchron an alle Listener schicken
  Iterator iterator = this.listenerList.iterator();
  while(iterator.hasNext())
  {
    FooListener listener = (FooListener)((WeakReference)iterator.next()).get();
    [b]// zusätzlich noch die Referenz prüfen[/b]
    if(listener != null)
    {
       listener.handleMessage....
    }
  }
}

Ich denke, ich bin nächste Woche gut paar Stunden dabei, das komplette Design
wieder gerade zu biegen. Dabei hat es ohne die InternalFrames wunderbar funktioniert.
Sch..., ich glaube nie wieder an das, was in der Java-API steht. :cry:

Verrückt, was? :wink:

Gruß,
Michael
 
B

Beni

Gast
Verrückt, was? :wink:
Naja, normalerweise stimmt es schon, was in der API steht. Ist nur manchmal ein bisschen schwer zum verstehen (an dieser Referenc-Geschichte stehe ich noch an...)

Aber wenn du alles neudesignest: mach Dir nichts draus, das Programm wird schlimmstenfalls besser und übersichtlicher :wink:
 
Status
Nicht offen für weitere Antworten.
Ähnliche Java Themen

Ähnliche Java Themen

Neue Themen


Oben