# Garbage Collector löscht anscheinend nichts



## RBS2002 (3. Nov 2010)

Hi,

hoffe das wurde nicht in anderen Threads behandelt - aber da ich mit OutofMemory Problemen, besonders nach Erzeugung von Listen und deren Befüllung, kämpfe hier einmal eine grundsätzliche Frage:

Ich möchte z.B. mit der folgenden Befehlsreihenfolge (kann sein das der Code so nicht funktioniert, schreibe es gerade aus den Kopf, aber das Anliegen sollte verständlich sein) die alte Referenz löschen und mit der neuen Referenz "überschreiben":


```
public void setListe(List neuereferenz)
{
altereferenz.clear(); //Lösche alle Einträge der Liste, zu mindestens das sollte doch Speicher frei räumen
altereferenz = null;
altereferenz = neuereferenz;
}
```

Nach meinem Verständniss müsste der GC nun, durch null, wissen das die Referenz nicht mehr benötigt wird und er diese löschen kann. Leider bläht sich das Programm aber nach jedem Aufruf immer weiter auf, egal ob ich die Extra-Parameter aufrufe oder nicht (mit Speicherzuweisungsparameter XS etc. läuft es natürlich länger, der Effekt bleibt aber der gleiche) - sodass man das Gefühl hat das der GC überhaupt nichts löscht.

Hier mal der grundsätzliche Ablauf des Programmes:

a) Thread (Server) wartet auf Anfragen - dieser läuft unendlich lange. Momentan kommen aber keine Clientanfragen rein womit er mit seinem Abarbeitungsserver kommuniziert und Objekte per Socket austauscht.
b) Ein zweiter Thread (Allgemeiner Abarbeitungsthread) wird gestartet. Dieser scannt, in einem neuen Thread, die Datenbanken und führt alle Abarbeitungen durch (Vergleich mit rechteliste, per E-Mail verschicken, Ergebniss an Hauptserver schicken etc.) - dieser wird, mit Hilfe von Quartz, stündlich gestartet und, wenn er fertig ist, beendet. In seinem Hauptthread kommuniziert er mit den Hauptserver.
c) In diesem zweiten Thread markiere ich alle "lokalen Referenzen" explizit. In den weiteren Abarbeitungsfunktion mache ich dies nicht (theoretisch müssten diese dort eh, mit der Entfernung der Hauptreferenz, automatisch mit markiert werden da sie ja zu diesen gehören (Vater - Kind Beziehung) - zu mindestens wenn die Funktion verlassen wird)
d) Alle Objekte werden über Locks synchronisiert. Wenn ein Thread ein Objekt anfordert wird es über einen Stream kopiert (damit er ab da an ohne Probleme damit arbeiten kann und keinen anderen thread - z.B. einen Client der die momentanen Daten abrufen will - behindert oder ein gleichzeitiger Zugriff provoziert wird), wenn es "überschrieben" wird, wird der oben genannte Algorithmus aufgerufen (die bearbeitete Kopie ersetzt in dem Fall das Original Objekt)

Ich denke mal das ich irgendwo ein Verständnissproblem zum garbage Collector habe und hoffe das mir jemand weiterhelfen kann - sollte es Verständnissprobleme bei der Beschreibung des Ablaufs geben bitte melden 

Vielen Dank im Voraus und mfG,
RBS2002


----------



## Wildcard (3. Nov 2010)

```
altereferenz.clear(); //Lösche alle Einträge der Liste, zu mindestens das sollte doch Speicher frei räumen
altereferenz = null;
altereferenz = neuereferenz;
```
Das ist völlig sinnfrei, 
	
	
	
	





```
altereferenz = neuereferenz
```
 reicht völlig. Der Garbage Collector kann Objekte abräumen die nicht mehr Stark referenziert werden, nicht mehr, nicht weniger.
Um den Grund für einen Memory Leak zu finden empfehle ich die Kombination aus Heap Dumb + Eclipse Memory Analyzer Tool (MAT). Damit hat man das Problem in der Regel in wenigen Minuten identifiziert.


----------



## RBS2002 (3. Nov 2010)

natürlich ist das sinnfrei  Zu mindestens war es damals ein Versuch das Speicherproblem zu lösen, was - wie bisher alles - fehlgeschlagen ist. Aber danke für den Tipp


----------



## Marcinek (3. Nov 2010)

Der GC läuft auch nur periodisch und macht das nicht instant bei jedem null oder so


----------



## Wildcard (3. Nov 2010)

Marcinek hat gesagt.:


> Der GC läuft auch nur periodisch und macht das nicht instant bei jedem null oder so


Allerdings läuft der GC in jedem Fall bevor sich die VM mit Out Of Memory verabschiedet. Entweder du brauchst tatsächlich mehr Speicher, oder du hast einen Memory Leak, da hilft dir wie gesagt MAT weiter.


----------



## RBS2002 (3. Nov 2010)

> Der GC läuft auch nur periodisch und macht das nicht instant bei jedem null oder so



Das soll er auch nicht, verbraucht ja auch Performance wenn der läuft - obwohl ich das durch die Threads eigentlich nicht merken sollte, oder?



> Allerdings läuft der GC in jedem Fall bevor sich die VM mit Out Of Memory verabschiedet. Entweder du brauchst tatsächlich mehr Speicher, oder du hast einen Memory Leak, da hilft dir wie gesagt MAT weiter.



Er meldet auch mehrmals solche Out of memorys (ab und zu schafft er sogar weniger/mehr bevor der Fehler den Job beendet), aber ab einem bestimmten Punkt kommt nur noch dieser Fehler.

ja, ich werde mir das mal ansehen (muss mal versuchen das ganze in Eclipse zu importieren, gibt es so etwas auch für Netbeans) - das ich mehr Speicher benötige kann sein, nur schreibt er auch im Notfall in den Swap Bereich und füllt diesen. Zu mindestens kann man damit einmal ordentlich das System auslasten, aber das soll eigentlich nicht mein Ziel sein.

Aber, um noch einmal nachzufragen - wenn ich eine Funktion verlassen werden die darin deklarierten Referenzen automatisch dereferenziert, oder?


----------



## Wildcard (3. Nov 2010)

> Er meldet auch mehrmals solche Out of memorys (ab und zu schafft er sogar weniger/mehr bevor der Fehler den Job beendet), aber ab einem bestimmten Punkt kommt nur noch dieser Fehler.


Die VM steigt mit Out Of Memory aus wenn der GC mehr als 90% der Rechenzeit verbrät und dabei kaum Speicher freigeben kann.



> ja, ich werde mir das mal ansehen (muss mal versuchen das ganze in Eclipse zu importieren, gibt es so etwas auch für Netbeans)


Du brauchst nichts in Eclipse zu 'importieren' ausser dem Heap Dump.


> das ich mehr Speicher benötige kann sein, nur schreibt er auch im Notfall in den Swap Bereich und füllt diesen.


Swapping erledigt das OS und das hat nichts damit zu tun. Der maximale Heap den die VM allokieren kann wird mit -Xmx geregelt, unabängig davon ob Teile davon geswapped werden.



> Aber, um noch einmal nachzufragen - wenn ich eine Funktion verlassen werden die darin deklarierten Referenzen automatisch dereferenziert, oder?


Referenzen selbst sind nicht dein Problem, es geht um die Objekte. Die können wie gesagt erst dann abgeräumt werden wenn keine starke Referenz mehr auf sie zeigt. Die 'schwachen' Referenzen Weak, Soft und Phantom Referenz verhalten sich etwas anders, aber du würdest wissen wenn du mit etwas anderem als starken Referenzen arbeitest.
Und ja, wenn du eine Variable innerhalb einer Methode deklarierst und diese Variable etwas referenzieren lässt, dann 'erlischt' die Referenz sobald die Methode verlassen wird, darüber musst du dir keine Sorgen machen und du musst auch nicht irgendetwas 'null' setzen.
Anders sieht die Sache natürlich bei Membern und statischen Variablen aus.


----------



## RBS2002 (3. Nov 2010)

danke, wird der Heap Dump nicht als Parameter gestartet? (Zu mindestens wird es hier so beschrieben Link) Oder meinst du etwas anderes? Das Eclipse Tool wird man ja sicherlich als Plugin laden können, oder?


----------



## Wildcard (3. Nov 2010)

Als Plugin oder Standalone Anwendung. Heap dumps kann man auf verschiedene Weisen erstellen. 
-programmatisch
-in intervallen
-per externer Applikation (Visual VM zB)

Am einfachsten dürfte es sein -XX:+HeapDumpOnOutOfMemoryError als Startup Parameter zu übergeben. Dann erstellt die JVM den Dump sobald der OutOfMemoryError fliegt.


----------



## Marco13 (3. Nov 2010)

Die VisualVM ist für sowas IMHO am praktischsten (wenn man nicht den TPTP installieren oder sogar Geld ausgeben will). Etwas versteckt im JDK\bin\ Verzeichnis, "jvisualvm.exe" starten....


----------



## Wildcard (3. Nov 2010)

> (wenn man nicht den TPTP installieren oder sogar Geld ausgeben will)


TPTP,... Igitt


----------



## RBS2002 (3. Nov 2010)

ok, danke


----------



## RBS2002 (6. Nov 2010)

Ich habe, da ich noch auf den Dump warte (das Programm läuft schon 3-4 Tage bis der Fehler kommt) nochmal zwei kleine Fragen 

a) Die Maschine auf der das Programm läuft hat momentan 1024MB Ram,davon werden vom System und den anderen laufenden Programmen ca.130MB belegt. Mein Programm läuft derzeit mit dem Parameter:


```
java -jar -Xms256m -Xmx900m -XX:MaxPermSize=256M -XX:MaxNewSize=128M -XX:+UseParallelGC Server.jar
```

Steckt da schon der Fehler im Detail und kann man noch etwas verbessern? Wie gesagt,der Speicher läuft - wenn das Programm entsprechend lange läuft - zu 100% voll, danach lagert er alles logischerweise in den Swap aus (das Programm lief früher unter einer Debian Kiste mit 2GB Ram, dort hat er aber ständig nur "Killed" geliefert und das Progamm selbstständig geschlossen, die neue ist da doch auskunftsfreudiger). Nicht das ich mit meinen Parametern den GC verhindere 

b) Weiß jemand wo ich dieses Programm unter Linux finden müsste oder ob es einen Befehl gibt um das rauszubekommen?


----------



## madboy (6. Nov 2010)

RBS2002 hat gesagt.:


> b) Weiß jemand wo ich dieses Programm unter Linux finden müsste oder ob es einen Befehl gibt um das rauszubekommen?




```
which jvisualvm
```
. Brauchst du aber normalerweise nicht, Aufruf von 
	
	
	
	





```
jvisualvm
```
 reicht bei korrekter Installation eines JDKs aus


----------



## tuxedo (8. Nov 2010)

Wenn der TO mit Netbeans arbeitet: Da gibts doch schon nen recht guten Profiler? Wozu der Umweg über MAT und Eclipse?

--> Google -> "Netbeans Profiler"


----------



## tuxedo (8. Nov 2010)

Wenn der TO mit Netbeans arbeitet: Da gibts doch schon nen recht guten Profiler? Wozu der Umweg über MAT und Eclipse?

http://profiler.netbeans.org/index.html


----------



## Wildcard (8. Nov 2010)

tuxedo hat gesagt.:


> Wenn der TO mit Netbeans arbeitet: Da gibts doch schon nen recht guten Profiler? Wozu der Umweg über MAT und Eclipse?


Weil der MAT die Leak Suspects direkt auflistet und es damit für Anfänger leichter wird das Leak zu identifizieren.


----------



## Marco13 (8. Nov 2010)

Sind der NetBeans profiler und jVisualVM nicht "unter der Haube" das gleiche? ???:L


----------



## RBS2002 (11. Nov 2010)

also ich habe mir jetzt mal mit VisualVM mein HeapDump angesehen (es ist ca. 900MB groß, kann es deshalb nicht hochladen). Kann besonders viel Speicher nehmen char[] und String Variablen ein. String ist ja vielleicht noch okay, aber char[] benutze ich, zu mindestens nicht absichtlich, eigentlich nicht. Jetzt mecket er, soweit ich das überblicken kann, immer über den ObjectOutputStream vom Socket - soweit ich mich belesen habe müsste ich diesen schließen um die Ressourcen wieder frei zu bekommen - aber dann würde ich ja die Verbindung bzw. den Stream verlieren... Oder gibt es da noch eine andere Möglichkeit. Momentan sieht mein Writer Objekt so aus:


```
/*
 * To change this template, choose Tools | Templates
 * and open the template in the editor.
 */

package Server_Client;

import java.io.IOException;
import java.io.ObjectOutputStream;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;

/**
 *
 * @author RBS2002
 */
public class Write{
    
    private ObjectOutputStream output;
    private Lock writer = new ReentrantLock();

    public Write(ObjectOutputStream output) {
        this.output = output;
    }

    public void write(Object object){
            writer.lock();
            try{
            output.writeObject(object);
        } catch (IOException ex) {
        }finally{
            writer.unlock();
        }
    }

    public void close(){
        try{
            output.close();
        }catch(Exception ex){}
    }
}
```

Wie gesagt, die Verbindung muss ständig laufen da es kein Client sondern die Verbindung zum Datenserver ist. Hoffe das wird jetzt nicht zu sehr Off-Topic - aber vielleicht ist es gerade die Lösung zu meinem Problem, zu mindestens wenn ich den Viewer und das Dump richtig lese 

MfG RBS2002


----------



## Wildcard (12. Nov 2010)

Hast du MAT ausprobiert? Der gibt dir wie gesagt einen Report der Leak Suspects.
Zum Object Output Stream: Der ObjectOutputStream cached alle Objekte die darüber serialisiert werden. Um den Cache zu leeren kann man wahlweise close oder reset aufrufen.


----------



## RBS2002 (12. Nov 2010)

Ist leider bis jetzt ein bisschen schwierig gewesen da es Eclipse und meine persönlichen Rechner bisher nicht geschafft haben irgendwelche Sockets aufzubauen und ich es dadurch nicht zum laufen kriege. Ich werde mir den HeapDump noch einmal morgen genauer ansehen. Das Tool was ich verwendet habe war schon dahingehend sehr hilfreich, zumal es auch gezeigt hat das die ganzen LinkedListen die ich zuerst in Verdacht hatte einen sehr kleinen Teil des Speichers ausgemacht haben. Bisher läuft das Programm, da es sowieso noch keinen brauchbaren Client gibt, ohne den Objektversand zum Hauptserver. Aber das mit den Cachen hört sich soweit ganz interessant an, währe in meinem Fall (ich will die Verbindung nicht verlieren) das 


```
public void write(Object object, Socket socket){
            writer.lock();
            try{
            output = new ObjectOutputStream(socket.getOutputStream());
            output.writeObject(object);
        } catch (IOException ex) {
        }finally{
            output.close();
            writer.unlock();
        }
    }
```

oder das besser:


```
public void write(Object object){
            writer.lock();
            try{
            output.writeObject(object);
        } catch (IOException ex) {
        }finally{
            writer.unlock();
           output.reset();
        }
    }
```

(Ich schätze mal das zweite - wenn ich jetzt von meinem Verständnis ausgehe hat der Socket ja nur einen OutputStream - und wenn ich den schließe dürfte ja der Stream und damit im Prinzip auch der Socket hinüber sein - oder wird durch die Übergabe ein neuer Stream erstellt der diesen Stream nur nutzt, die Daten bei sich cachet und der eigentliche Stream, also der Stream vom Socket, nichts davon mitbekommt und auch nach der Schließung des ObjectOutputStreams noch weiterläuft), aber vielleicht mache ich da grundsätzliche Fehler)


----------



## HoaX (13. Nov 2010)

Im Prinzip ist beides schlecht, da leerer catch-Block enthalten.


----------



## RBS2002 (13. Nov 2010)

ja, das ist richtig - aber ich denke es ist nicht sinnvoll 100 Zeilen Catch Code hier reinzuschreiben wenn es um ein ganz anderes Problem geht 

Ich glaube hier wird das ganze ganz gut beschrieben:

Resetting ObjectOutputStream - Java Tutorials

ich denke mal ich werde das so machen


----------

