Lotus Notes / Domino Sonstiges > Java und .NET mit Notes/Domino

LS2J Schnittstelle - memory leak

(1/4) > >>

Manfred Dillmann:
Hallo Axel,

zu Deinem Stichwort "memory leak" aus Deinem letzen Post:

Ich hatte bei meinem RSS Reader ein ebensolches Problem - obgleich ich keine Domino-Objekte verwende. Nach ca. 100 Läufen des Agenten auf dem Domino Server war mit dem Fehler:

Agent 'Fetch News Background' error: LS2J Error: Threw java.lang.OutOfMemoryError

dann Schluss.

Übliche Dinge wie Objects auf null setzen und System.runFinalization(); bzw. System.gc(); (war ein Tipp auf meine Frage im notes.net 6er Forum) haben etwas Linderung gebracht - richtig geholfen hat das alles aber nicht.

Nach vielen weiteren vergeblichen Versuchen habe ich dann versucht, statt einer Übergabe der Parameter( urlToLoad, ...) und der Rückgabe (return bufResult.toString();) die Funktion in der folgender Form zu gestalten:

public class GetRSSFeed {
   
   public String urlToRead;
   public String proxySet;
   public   String proxyHost;
   public   int proxyPort;
   public   String proxyUser;
   public String proxyPassword;
   public String returnURLContent;
   
   public void getRSSFeed() {
   ...
   returnURLContent = bufResult.toString();
   return;
   }
}

Ich setzte und lese dann diese Properties via LS (zwischendurch natürlich den Aufruf der Function). Ergebnis: der Agent läuft jetzt bei mir seit nahezu 2 Tagen non stop im 5 Minuten Takt ohne Speicherprobleme.

Axel, hast Du einer Erklärung ob das ein BUG in der Implementierung der LS2J Schnittstelle sein kann oder ob es daran liegen könnte, dass "String" in Java keiner der primitiven Datentypen ist?

P.S.
Mein RSS Reader funktioniert jetzt prima - offensichtlich auch durch eine Firewall - das wolltest Du mal in einem früheren Post wissen - kannst Du jetzt ja mal testen...

Gruss
Manfred

Axel_Janssen:
Hallo Manfred,

erstmal Glückwunsch zur Ausschaltung des Störfeuers. ;D

Ich habe mich noch nicht mit LS2J beschäftigt. Wenn ich mit Java in Lotus arbeite ist es bislang immer pur-Java (etwa ein Agent, 1 Applet, 1 GUI, die von aussen zugreift).
Du hast da einen interessanten Punkt aufgebracht. Und das ist leider wieder so ein Ding, wo offenbar etwas nicht so sauber von Lotus implementiert bzw. dokumentiert wird.

Durch

--- Code: ---return bufResult.toString()

--- Ende Code ---
wird ein Objekt erzeugt, dass den von der JavaVM verwalteten Heap Speicher belegt. Genau gesprochen ist es ein anonymes, lokales Objekt, da es von keiner Referenzvariable gehalten wird und Methoden-scope hat (keine Instanzvariable in ersten Version).
In einem reinen Java Programm würde das keine Probleme machen. Nach dem return der Methode wäre das Objekt eligible for garbage collection und der entsprechende Speicher würde zu einem geeigneten Zeitpunkt vom Garbage Collector freigegeben. Hmm genau gesprochen kann aus dem aufrufenden code das Objekt gehalten werden. Aber das ist in der Regel kein Problem... Arbeite später an umfangreicheren Erklärung...

Offenbar hält aber LS2J aus für mich derzeit nicht nachvollziebaren Gründen eine Referenz auf ein Objekt, das von einer Methode zurückgegeben wird, die einen nicht-void, nicht-primitiv-Datentyp (int, fload, boolean, etc.) besitzt. Solange eine Referenz auf das Objekt gehalten wird, ist es nicht "eligible for garbage collection".  

Du hast den richtigen Weg gefunden. Du schreibst den String einfach in eine Instanz-Variable, die du pro Durchlauf überschreibst.

Beispiel:
1. Durchlauf:
after completion method void getRSSFeed():
Object-Instanz-Variable GetRssFeed.returnURLContent hält String-Objekt "foo".
2. Durchlauf:
after completion method void getRSSFeed():
Object-Instanz Variable GetRssFeGetRssFeed.returnURLContent verliert Referenz auf String-Objekt "foo" und hat neue Referenz auf neues String-Objekt "bar". Das String-Objekt "foo" ist eligible for Garbage Collection. "bar" wird referenziert.
usw.  


Da der von der Java Virtual Maschine verwaltete Heap Speicher nicht mehr volläuft, schlägt der Java Garbage Collector in der neuen Version offenbar irgendwann zu.  

Sierra/Bates Buch hat sicher ein Kapitel zu den Grundlagen von Java Garbage Collection  ;D

Das hört sich alles komplizierter an als es ist.
Noch was: Bei Methoden mit void-Rückgabe benötigst du kein return. Das letzte return in der Methode kannst du entfernen.

Wie du weisst gibt es keine dummen Fragen, wenn du noch Fragen hast.

Ein anderer Weg wäre vielleicht mit einem Singleton zu arbeiten (später mehr).  

Gruß Axel

Axel_Janssen:

--- Zitat von: Axel_Janssen am 17.01.04 - 10:30:44 ---
Ein anderer Weg wäre vielleicht mit einem Singleton zu arbeiten (später mehr).  

--- Ende Zitat ---

Hmm. LS2J behandelt das Objekt der Klasse GetRSSFeed offenbar als Singleton. Du erzeugst ja aus dem code immer neue Objekte der Klasse GetRSSFeed. Nur ist LS2J offenbar so schlau, dass es immer das selbe Objekt wiederverwendet. Sonst würde das mit dem Überschreiben der Instanzvariable returnURLContent so gar nicht funktionieren.

Dann wären nämlich mehrere Objekte vom Typ GetRSSFeed im Heap und es würden keine Instanz-Variablen überschrieben.

Singleton ist ein Design-Pattern, das benutzt wird, wenn man von einer Klasse genau 1 Objekt haben will.

Es wird so implementiert:


--- Code: ---public class ASingleton {

// Klassenvariable. An Klasse nicht Objekt gebunden (static)
private static final Elvis INSTANCE = new Elvis();

// Konstruktor ist private. Kann nur aus Klasse selbst aufgerufen werden, nicht von aussen
private Elvis() {}

// das kann von aussen angesprochen werden. Gibt aber immer den selben Elvis zurück
public static Elvis getInstance() {
return INSTANCE;
}
}

--- Ende Code ---

wenn man in der Klasse mit Objekt-Serialisierung arbeitet, sind weitere Sachen zu beachten.

Übrigens kannst du deine Methode getRSSFeed() vielleicht auch als static-Methode implementieren und weiter mit String-Rückgabewert arbeiten.

Gruß Axel

Manfred Dillmann:
Hallo Axel,

danke für Deine Ausführungen - verstehe mal wieder nicht alles, aber das wird schon... ;)

>>erstmal Glückwunsch zur Ausschaltung des Störfeuers.<<

Ja, da habe ich mich wirklich gefreut. Zumal auf meine Frage im 6er notes.net Forum nicht mal ansatzweise ein Lösung kam. Habe dort aber heute meinen Weg gepostet (meine Frage war am 14.01 wenn Du´s dort mal lesen möchtest).

Du schreibst:
>>LS2J behandelt das Objekt der Klasse GetRSSFeed offenbar als Singleton. Du erzeugst ja aus dem code immer neue Objekte der Klasse GetRSSFeed. Nur ist LS2J offenbar so schlau, dass es immer das selbe Objekt wiederverwendet. Sonst würde das mit dem Überschreiben der Instanzvariable returnURLContent so gar nicht funktionieren.<<

Nö, mache ich nicht. Es wird nur EIN Object erstellt und immer wieder referenziert. Das sieht in LS so aus:
Dim js As JavaSession
Set js = New JavaSession
   
Dim getRSSFeedClass As JavaClass
Dim getRSSFeedObject As JavaObject
   
Set getRSSFeedClass = js.GetClass("GetRSSFeed")
Set getRSSFeedObject = getRSSFeedClass.CreateObject ' 1x Object erstellen/instanzieren
...
' Properties wie URL, Proxy usw. setzen
...
Call getRSSFeedObject.getRSSFeed() ' Aufruf mit IMMER gleichem Object in einer Schleife
Set jProperty = getRSSFeedClass.GetProperty("returnURLContent")
html = jProperty.GetValue( getRSSFeedObject )

Da ist LS2J wohl doch nicht so schlau...?

Vielleicht noch was für den Java-Crack zum schmunzeln, ich selbst kann mir das auch nicht richtig erklären:
Ich hatte ja aus lauter Verzweiflung alles mögliche probiert und bin noch auf den folgenden Effekt gestossen, vielleicht hast Du dafür eine Erklärung?

Als ich statt:
return bufResult.toString()

das hier:
String URLContent = bufResult.toString();
return URLContent;

verwendet hatte, war die Laufzeit des Agenten schon mindestens doppelt so lange wie vorher. Für einen Moment dachte ich, das wäre schon die Lösung, hat aber den Tot nur etwas verzögert...

Gruss
Manfred

Axel_Janssen:
Die ganze Geschichte ist ein bischen misteriös.
Es gibt eine reine Anwendungsentwickler-Sicht. Lotus stellt dieses LS2J zur Verfügung und der Entwickler kann nach bestimmten Regeln Java Objekte erzeugen, Methoden aufrufen etc.
In Wirklichkeit gibt es darunter eine systemische Schicht. Von dieser Komplexität soll der Anwendungsentwickler nix merken.
In Wirklichkeit sind das ja Kommunikationen zwischen zwei unterschiedlichen Prozessen. Die JVM läuft in einen anderen Prozess als Notes.
Wie wird jetzt die Connectivity zwischen den beiden Prozessen hergestellt?
Solange ich da keine Texte haben, die LS2J aus einer systemischer Sicht beschreiben, betreibe ich Kaffeesatzleserei.
Manchmal kommt man damit zu Ergebnissen, hier nicht.

Es soll aber nicht der Eindruck entstehen, dass dies Probleme sind, mit denen sich ein Entwickler von Java-Anwendungen normalerweise herumschlägt. Das macht gerade den Reiz aus.

Manfred, die sauberste Lösung wäre vermutlich überhaupt keine Objekte zu erstellen und alles über static abzufackeln (static Instanzvariblen, static Methoden).

Wobei deine Lösung natürlich völlig in Ordnung ist.

Beispiel:

--- Code: ---public class AllStatic {

public static String aVar = "hier";

public static String getMsg() {
  return "ist alles static";
}

public static void main (String args) {
System.out.println(AllStatic.aVar + " " + AllStatic.getMsg());
}
}

--- Ende Code ---

Es wird kein Objekt instanziiert. Da deine Klasse nur aus einer Methode besteht, könntest du dort genau so vorgehen.


--- Code: ---Set getRSSFeedClass = js.GetClass("GetRSSFeed")
' nächstes nicht mehr nötig
'Set getRSSFeedObject = getRSSFeedClass.CreateObject ' 1x Object erstellen/instanzieren
...
' Properties wie URL, Proxy usw. setzen und zwar über getRSSFeedClass.Property="aProperty";
' Methoden-Header in Java: public static String getRSSFeed()
html=getRSSFeedClass.getRSSFeed();

--- Ende Code ---

ist total nicht Objekt-orientiert. Da der Java code aber eine genau definierte enge Aufgabe erfüllt, die in Zukunft nicht erweitert werden soll (wüsste nicht warum), ist dies vermutlich die pragmatischste Lösung.

Gruß Axel

Gruß Axel

Navigation

[0] Themen-Index

[#] Nächste Seite

Zur normalen Ansicht wechseln