Zur Laufzeit erben?

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

tuxedo

Gast
Hallo zusammen,

für meine SIMON Bibliothek (siehe Signatur), bräuchte ich folgendes Feature (oder eben etwas vergleichbares):

Ähnlich wie bei RMI habe ich auf Client-Seite, meiner Client-Server-Anwendung "Remote Objekte" die für den Client den Server darstellen. Diese Remoteobjekte sind via Proxy und einer Socketverbindung mit dem Server "verbunden".

So. Nun möchte ich, dass, wenn der GC des Clients alle "Remote Objekte" aufgeräumt hat, die Socketverbindung getrennt wird. Nach ein wenig googeln bin ich auf die "finalize" Methode von "Object" gestoßen. Diese müsste ich in meinem RemoteObjekt einfach überschreiben und damit Rückmeldung an SIMON geben dass diese Instanz nun nicht mehr gebraucht wird. Ist auch sonst keine mehr "in gebrauch", wird die Verbindung getrennt.

Soweit so gut. Nur will ich den Entwickler nicht zwingen von einer "speziellen" Simon-Basisklasse zu erben, die "finalize()" für Simon implementiert hat. Bisher reicht es aus, wenn die Implementierung des Remoteobjekts irgendwo ein "implements SimonRemote" (ein Marker-Interface ohne Methoden, ähnlich wie "Serializeable") drin hat.

Gibt es eine Möglichkeit ein beliebiges Objekt (lässt sich anhand eines "implements SimonRemote" identifizieren) zur Laufzeit von einer bestimmten Klasse erben zu lassen, die "finalize()" passend implementiert?

Ich hoffe ich konnte das ganze verständlich formulieren ;-)

Bin gespannt auf Eure Ideen/Vorschläge.

Gruß
Alex
 
Das sollte mit AOP zu realisieren sein. Also nicht erben, sondern einen Aspekt an alle SimonRemote-Objekte implementieren. Bytecodemanipulation ist auch eine nette Schweinerei ;-)

Eventuell ist das in deiner Erklärung zu kurz gekommen:
wenn diese Objekte über das Proxyobjekt mit einer konkreten Socketverbindung in Verbindung stehen, werden sie dann überhaupt vom GC abgeräumt?
 
Dynamisches Erben:
Proxy und InvocationHandler.
finalizer sind allerdings des Teufels.
Um festzustellen wann ein Objekt fertig zum einsammeln ist, verwendet man PhantomReferences.
 
@FArt

ja, kam mir auch eben in den Sinn. Hab testweise "finalize" in der Proxy-Klasse eingebaut. Aber die wird ja nicht abgeräumt da noch Referenzen zu meiner Dispatcher-Klasse etc. bestehen, welche dann wiederum in direkter Verbindung mit der Socketverbindung stehen. Ergo würden diese nur dann abgeräumt wenn die Socketverbindung flöten geht.

Dumme Sache. Denke das "sauberste" wird es sein, wenn ich in meiner Bibliothek ein "release(SimonRemote)" anbiete, mit der man selbst dafür sorgen muss, die RemoteObjekte bei "wird nicht mehr verwendet" (programmbeenden oder so) abzumelden/frei zu geben.

Intern kann ich dann ja checken ob alles freigegeben wurde und kann die Verbindung dann bei bedarf trennen.

Aber ein "automatismus" wäre da eben schick ...
Hast du zu AOP nen guten Einführenden Link? Wenn nicht bemühe ich mal google.

@tfa

Ja, die Warnung vor finalizern hab ich gelesen/gesehen/kenne ich. Aber wie überall gibt es unterschiedliche Meinungen. Und eine Meinung vertritt es eben, wenn man mit finalizern Socketverbindungen aufräumt oder native Dinge (geladene DLLs, irgendwelche IO-Handles) freigibt...

Das was du mit dem Thread beschreibst hab ich Serverseitig schon laufen. MIr geht's jetzt drum, dass der Client terminiert wenn keine Remote-Objekte mehr in Benutzung sind. Bisher terminiert der Client eben nicht, weil die Socketverbindung noch da ist. Wie gesagt, es geht um die Clientseite.


- Alex
 
Nein, aber es gibt Fälle in denen ich einer DLL explizit sagen möchte: "Du, räum mal deine offenen Handles, GDIs und sonstigen Kram auf, wir wollen jetzt Feierabend machen."

Werd jetzt mal nach "PhantomReferences" googeln. Die sagen mir bisher noch gar nix.

- Alex
 
alex0801 hat gesagt.:
Aber die wird ja nicht abgeräumt da noch Referenzen zu meiner Dispatcher-Klasse etc. bestehen, welche dann wiederum in direkter Verbindung mit der Socketverbindung stehen. Ergo würden diese nur dann abgeräumt wenn die Socketverbindung flöten geht.
Klingt danach, als würden WeakReferences das Problem lösen.

Ansonsten kannst Du Dir mal CGLib angucken. Viele Frameworks wie Hibernate oder Spring benutzen teilweise diese Library für Ihre Magie.

Edit: Zur Laufzeit Vererben ist aber keine gute Idee, weil Du damit die User einschränkst. Denn was machst Du, wenn die Klasse schon von einer anderen erbt? Dann könntest Du auch gleich aus SimonRemote eine abstrakte Klasse machen.
 
alex0801 hat gesagt.:
Das was du mit dem Thread beschreibst hab ich Serverseitig schon laufen. MIr geht's jetzt drum, dass der Client terminiert wenn keine Remote-Objekte mehr in Benutzung sind. Bisher terminiert der Client eben nicht, weil die Socketverbindung noch da ist. Wie gesagt, es geht um die Clientseite.
So einen Thread kannst du doch auch auf Clientseite laufen lassen.

Aber wie überall gibt es unterschiedliche Meinungen. Und eine Meinung vertritt es eben, wenn man mit finalizern Socketverbindungen aufräumt oder native Dinge (geladene DLLs, irgendwelche IO-Handles) freigibt...
Gerade bei solchen Dingen sind Finalizer besonders gefährlich. Niemand sagt dir, ob und wann finalize() aufgerufen wird. Es kann gut passieren, dass dein letztes RemoteObject beendet wird und noch eine Stunde untot auf dem Heap liegen bleibt, weil der GC nicht anspringt. Deine Connection ist dann genausolange offen.
Ressourcenmagement muss man in Java leider von Hand erledigen. Da gibt's keine Alternative. Vielleicht werden ja irgendwann mal ARM-Blöcke eingeführt wie in C#, das würde die Sache etwas vereinfachen.
 
@tfa

Wie soll ich denn auf Clientseite mit einem Thread generisch entscheiden können, ob noch irgendwo ein RemoteObjekt in Benutzung ist?

Sieht aber gerade so aus, als ob ich mit WeakReference in meinem Proxy-Objekt (das ist ja das Remote-Objekt), alle Referenzen zum Dispatcher etc,. abbilde, und dann mit PhantomReference das "Objekt nicht mehr in Benutzung" abfangen und damit die Verbindung trenne.

Mal schauen. Muss da erst einiges zu lesen...
 
alex0801 hat gesagt.:
Wie soll ich denn auf Clientseite mit einem Thread generisch entscheiden können, ob noch irgendwo ein RemoteObjekt in Benutzung ist?
Die Objekte müssen natürlich irgendwo registriert sein und sich abmelden, wenn sie nicht mehr benutzt werden. Wie gesagt, automatisch geht das nicht, man muss es schon programmieren.
 
Weak-References ist Käse, hier nicht zu gebrauchen.

Alex geht es um eine Optimierung, nicht um zugesichertets Verhalten (Veto wenn das nicht stimmt). Ergo: finalize ist nicht mehr evil.

Selber einen Thread laufen lassen, Phantomreferenzen überprüfen.... habe ich schon mal gehört... ach ja, macht die VM und ruft dann finalize auf... aber wenn ich es selber mache ist es ja vielleicht besser ;-), ok, ich könnte im finalize starke Referenzen wieder anlegen...

Auch sonst tut man sich mit dem Thread schwer, denn das Kriterium für die Aktion ist einfach nicht festgelegt.
 
Jupp, WeakReference's haben dieses eine Problem gelöst.

Ich hab in meinem InvocationHandler der für den Proxy verwendet wird, alle weiteren abhängigen Referencen mit WeakReferencen ersetzt.

Wenn ich jetzt "manuell" mein Remoteobjekt auf "null setze" (remoteObject = null😉 und "System.gc();" triggere, wird (testweise) die "finalize()" Methode aufgerufen. Das funktioniert also...

So, jetzt muss ich nur noch finalize() mit PhantomReference ersetzen und schon bin ich am Ziel.

Dabei hab ich jetzt aber ein Problem:

Zum erzeugen des Proxys brauchts ein Objekt, das "implements InvocationHandler" beinhaltet. Das ist ja kein Ding. Hab ich ja schon. So, aber um jetzt mittels PhantomReference "mitzubekommen", wann das erzeugte Proxyobjekt abgeräumt/nicht mehr gebraucht wird, müsste ich dieses Proxy-Objekt in eine PhantomReferenz stecken. Aber der Client soll das ja nicht sehen/wissen. Für ihn soll es nach wie vor ein Remote-Objekt sein/bleiben.

Wie schalte ich jetzt also "PhantomReference" zwischen meine "InvocationHandler" implementierende Klasse "SimonProxy" und den "myRemoteObject = Proxy.newInstance(...,...,mySimonProxy)" Aufruf?

Entweder ich steh auf dem Schlauch, oder das geht so gar nicht und ich muss mir was anderes überlegen.

- Alex
 
Also, so wie ich das grade verstehe, sind das "zwei Paar Schuhe" die du da verwechselst 😉

- Entweder du verwendest einen Proxy, um den Objekten dynamisch die finalize()-Methode zu geben. Dann räumst du in der finalize-Methode auf

- Oder: mehr Arbeit, dafür evtl. sicherer (obwohl der GC theoretisch nix anderes macht) - du speicherst dir nur Weak-/Phantomreferences auf deine Objekte. Sobald diese nicht mehr existieren, weißt du, dass das Objekt nicht mehr benötigt wird, und kannst aufräumen.
 
byto hat gesagt.:
Weak-References lösen doch genau das von Dir angesprochene Problem: ???:L

*zurück ruder*
Jawoll, Käse ist Käse. Kommt wenn ich das richtig sehe darauf an, wie die genaue Verwendung ist.

Die Weak-Reference liefert null, wenn das Objekt nicht mehr referenziert wird... aber die Frage ist ob das gegeben ist bzw. ob ich alle Objekte in eine Weak-Referenz stopfen kann, um die es geht.

Die Phantomreferenzen sind nur besser, wenn sichergstellt sein soll, dass im finalize() keine harten Referenzen auf das Objekt wieder erstellt werden... kann das hier passieren?
 
finalize ist ebenfalls ein Problem, wenn eine Exception fliegt. Das Objekt verbleibt dann als Speicherleiche.
Sollte durch die angesprochene harte Referenz das Objekt wieder auferstehen, wird der finalizer kein zweites mal aufgerufen und die zu schließenden Resourcen werden nicht geschlossen.
Alles in allem, recht gefährlich.
 
Okay, ich denke um zu veranschaulichen wo es jetzt noch hängt, muss ich etwas weiter ausholen:

SIMON macht prinzipiell genau das, was RMI auch macht: Ein Client holt sich bei einem Server ein Remote-Objekt ab, damit er dessen Methoden lokal verfügbar hat. Dieses Remote-Objekt ist ein Proxy-Objekt, welches die Methodenaufrufe über's Netzwerk zum Server "umleitet", welcher die Methode lokal bei sich ausführt und das Ergebnis zurück an den Client schickt.

So.

Alle Remoteobjekte werden also durch Proxys realisiert. Um einen Proxy zu erzeugen, brauch ich einen InvocationHandler. Der sieht, exemplarisch so aus:

Code:
public class SimonProxy implements InvocationHandler {
	
	// meine "WeakReferenzen"
	private WeakReference<Dispatcher> dispatcherReference;
	private WeakReference<SelectionKey> keyReference;
	
	// Der Konstruktor in dem die Hard-Referenzen in WeakReferenzen eingebettet werden
	public SimonProxy(Dispatcher dispatcher, SelectionKey key, String remoteObjectName) {
		dispatcherReference = new WeakReference<Dispatcher>(dispatcher);
		
		this.remoteObjectName = remoteObjectName;

		keyReference = new WeakReference<SelectionKey>(key);
	}

	// Die Invoke-Methode, die die WeakReferenzen letztendlich nutzt
	public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
		
		// tu hier etwas blabla foobar

		// übermittle den Methodenaufruf an den Server und warte dessen Atwort ab
		Object result = dispatcherReference.get().invokeMethod(keyReference.get(), remoteObjectName, 
Utils.computeMethodHash(method), method.getParameterTypes(),args, method.getReturnType());
		
		// tu hier noch mehr blabla foobar

		return  result;
	}

	// nur zum testen ...	
	@Override
	protected void finalize() throws Throwable {
		// TODO Auto-generated method stub
		super.finalize();
		System.out.println("Proxy wird aufgeräumt");
	}
	
}

Soweit also der Invocationhandler. Erzeugt wird das Proxy-Objekt so:

Code:
	public static SimonRemote lookup(String host, int port, String remoteObjectName) throws SimonRemoteException, IOException, EstablishConnectionFailed {
		SimonRemote proxy = null;
		
		Dispatcher dispatcher = new Dispatcher(lookupTable,getThreadPool());
		
		Thread clientDispatcherThread = new Thread(dispatcher,"Simon.Dispatcher");
		clientDispatcherThread.start();

		try {
			Client client = new Client(dispatcher);
			client.connect(host, port);

			Class<?>[] listenerInterfaces = (Class<?>[]) dispatcher.invokeLookup(client.getKey(), remoteObjectName);

			// Hier wird der InvocationHandler ins Leben gerufen
			SimonProxy handler = new SimonProxy(dispatcher, client.getKey(), remoteObjectName);
			
			// und hier wird daraus ein Proxy-Objekt gemacht
			proxy = (SimonRemote) Proxy.newProxyInstance(SimonClassLoader.getClassLoader(Simon.class), listenerInterfaces, handler);
		    
		} catch (LookupFailedException e) {
			throw new LookupFailedException(e.getMessage());
		} catch (IOException e){
			throw new ConnectException(e.getMessage());
		}
		
		return proxy;
	}

So. Am Client kann ich jetzt folgendes tun:

Code:
// "server" ist das Remote-Objekt, welches mittels Proxy.newInstance(...) und dem InvocationHandler (SimonProxy) gebaut wurde
ServerInterface server = (ServerInterface) Simon.lookup("localhost", 2000, "server");
server.login(clientCallbackImpl);

Funktioniert alles prima. Der Client ruft die Login-Methode am Server auf. Testweise wird danach das ServerObjekt NIE WIEDER benutzt (Die main() Methode ist nach diesen Code-Zeilen schon zuende...). Es darf also vom GC aufgeräumt werden. Aber das tut es "noch" nicht von alleine (zumindest hat's das in meinem Test nicht. Irgendwelche Ideen warum?). Also hab ich den Test "erweitert":

Code:
// "server" ist das Remote-Objekt, welches mittels Proxy.newInstance(...) und dem InvocationHandler (SimonProxy) gebaut wurde
ServerInterface server = (ServerInterface) Simon.lookup("localhost", 2000, "server");
server.login(clientCallbackImpl);
server = null;
System.gc();

In der Console seh ich nun die Meldung "Proxy wird aufgeräumt", also die von der finalize() Methode im InvocationHandler. Abgesehen davon, dass ich das Objekt selbst auf null gesetzt und den GC von Hand aufgerufen habe, ist das schon fast so wie ich es gerne hätte. Nun ja, finalize() ist ja "evil" und es gibt, wie von euch bereits erwähnt, bessere "Techniken", z.B. "PhantomReference".

PhantomReferenz lässt sich, so wie ich das verstanden habe, genau so wie "WeakReference" verwenden, mit dem "Unterschied", dass man die ReferenceQueue die man beim erzeugen mitgeben kann überwachen sollte, und damit dann "mitbekommt" wann das Objekt nicht mehr weiter referenziert wird. Anhand dieser Info könnte ich dann meine Socketverbindung abbauen, sofern es keine weiteren lebenden RemoteObjekt-Instanzen gibt.

Prima. Soweit hab ich das verstanden. Was ich jetzt nicht verstehe ist, wie ich meinen InvocationHandler SimonProxy mit der PhantomReference und Proxy.newInstance(...) verheirate, so dass für den Entwickler das RemoteObjekt immer noch RemoteObjekt ist und die Sache mit der PhantomReference von SIMON intern gedeckelt wird.

Ich hoffe das war nun ausführlich genug. Bin für jeden Tipp offen ;-)

- Alex
 
Der GC räumt Objekte nicht ab, wenn sie nicht mehr referenziert werden, sondern er läuft nur los, wenn es (seiner Meinung nach) sinnvoll ist und räumt dann die Objekte ab, die nicht mehr referenziert werden.

Wenn ich den Code hier richtig interpretiere, machen die WeakReferences in deinem Stub doch keinen Sinn, oder?

Es wird die Referenz auf den Dispatcher und die Client ID gehalten. Wenn der Stub nicht mehr referenziert wird, und er der einzige ist, der diesen Dispatcher und die Client ID hält, dann wird auch mit normalen Referenzen abgeräumt (vorausgesetzt der GC will gerade).

Was passiert mit dem (und in dem) Dispatcher Thread, oder kann der für diese Betrachtung vernachlässigt werden?
 
FArt hat gesagt.:
Der GC räumt Objekte nicht ab, wenn sie nicht mehr referenziert werden, sondern er läuft nur los, wenn es (seiner Meinung nach) sinnvoll ist und räumt dann die Objekte ab, die nicht mehr referenziert werden.

Ja, das weiß ich schon. Nur hätte ich nicht vermutet, dass der GC sich bei meinem 5 Code-Zeilen main() Programm über 2min Zeit lässt (habs abgebrochen, denke der wäre nicht gelaufen), selbst wenn keine weitere Codezeile in der main() Methode mehr folgt und folglich das RemoteObject (welches sonst nirgends mehr referenziert wird) nicht mehr gebraucht wird.

Wenn ich den Code hier richtig interpretiere, machen die WeakReferences in deinem Stub doch keinen Sinn, oder?

Es wird die Referenz auf den Dispatcher und die Client ID gehalten. Wenn der Stub nicht mehr referenziert wird, und er der einzige ist, der diesen Dispatcher und die Client ID hält, dann wird auch mit normalen Referenzen abgeräumt (vorausgesetzt der GC will gerade).

Das ist ja das "verzwickte": Der Key und der Dispatcher werden nur solange gebraucht wie das RemoteObjekte gebraucht wird. Wird das RemoteObjekt nicht mehr gebraucht muss ich das "mitbekommen" und darauf hin die Threads die irgendwo hinter dem Dispoatcher vergraben sind beenden und damit die Netzwerkverbindung zu kappen.

Was passiert mit dem (und in dem) Dispatcher Thread, oder kann der für diese Betrachtung vernachlässigt werden?

Hab ich gerade geschrieben...

Das RemoteObjekt hängt am Dispatcher. Das RemoteObjekt wird vom GC nicht von selbst erfasst, da der Dispatcher noch läuft. Aber den will ich automatisch beenden (komme noch über einen anderen Weg als das RemoteObjekt an den Dispatcher dran) wenn keiner mehr das RemoteObjekt braucht. Dachte mit der WeakReference krieg ich den Dispatcher (und den Key) vom RemoteObjekt entkoppelt. Und so wie ich die API Doc verstande müsste das auch so funktionieren.

- Alex
 
Ich glaub ich brauch nen extra Thread um das mit den WeakReferenzen zu klären. Krieg so langsam aber sicher nen Knoten im Hirn ...

Also an dieser Stelle mal Danke für eure Hilfe.

- Alex
 
Status
Nicht offen für weitere Antworten.

Neue Themen


Zurück
Oben