Problem mit Eclipse - WindowBuilder - memoryLeak?

U156

Mitglied
Hallo in die Runde,
ich bin neu im Forum. Ich bin 44 Jahre und beschäftige mich Hobbymässig mit der Programmierung. Jetzt bin ich bei Java gelandet und hab ein Problem mit einem MemLeak. Wie gesagt, ich arbeite seit ca. 2 Wochen mit Java. Ich habe mich für Ecplise in der Version: 2022-12 (4.26.0) entschieden. Das macht es mir einfacher via WindowBuilder schnell Ergebnisse zu erzielen.

Ich hab mir ein kleines Hilfstool gebaut und nachdem es lief hab ich festgestellt, dass der Arbeitsspeicher kontinuierlich größer wird. Ich habe das ganze Programm rauf und runter durchgewühlt und nichts gefunden. Also hab ich eine einfache Testumgebung angelegt.

Neues Java Projekt --> package View --> Klasse TestView Application Window via WindowBuilder eingebunden und das leere Window gestartet.

Java:
//komplett von Eclipse erzeugter Code!

package View;
import java.awt.EventQueue;
import javax.swing.JFrame;

public class startView {

    private JFrame frame;

    /**
     * Launch the application.
     */
    public static void main(String[] args) {
        EventQueue.invokeLater(new Runnable() {
            public void run() {
                try {
                    startView window = new startView();
                    window.frame.setVisible(true);
                } catch (Exception e) {
                    e.printStackTrace();
                }
            }
        });
    }

    /**
     * Create the application.
     */
    public startView() {
        initialize();
    }

    /**
     * Initialize the contents of the frame.
     */
    private void initialize() {
        frame = new JFrame();
        frame.setBounds(100, 100, 450, 300);
        frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
    }
}

Nach dem Start kann man im Arbeitsspeicher das gleiche verhalten beobachten. Das Programm verbraucht kontinuierlich mehr Arbeitsspeicher. Nach zwei Std ist das Programm von ca.40Mb auf 100Mb gestiegen und das bei einem leeren Window.
Mit dem Programm JProfiler in der 9Tage Testversion konnte ich das Problem weiter eingrenzen und den Verursacher ermitteln. Siehe Anhang.
By the Way - gibt es andere LiveTime Analyse Tools? Oder weiss jemand was der JProfiler kosten soll. Hab dazu nichts im Netz gefunden.

Das java.Lang.Objekt wurde als Hauptverursacher ausgemacht. Dieser erzeugt unmengen an Instanzen. Ich kann mir nur nicht erklären warum. Das JProfiler Programm ist mir auch nicht so vertraut um hier weiter in die tiefe zu kommen. Und wenn, es scheint ein Bug seines Java zu sein, oder wie seht Ihr das?

Für alle die Lust haben, ich hab im Anhang mal alle notwendigen File angefügt. Vielleicht habt ihr ein Idee.

Hinweis. Das MemLeak sofern es sich um eins handelt tritt sowohl unter Window als auch unter Mac auf. Auch hab ich verschiede Java Versionen probiert. Zum eine mit Java SE-1.8. und auch mit JavaSE-19. Immer das gleiche Ergebnis.

Vielen Dank im Voraus vielleicht kann mich jemand erleuchten.

LG David.
 

Anhänge

  • Bildschirm­foto 2022-12-10 um 12.38.05.png
    Bildschirm­foto 2022-12-10 um 12.38.05.png
    743,4 KB · Aufrufe: 2
  • Bildschirm­foto 2022-12-10 um 12.40.30.png
    Bildschirm­foto 2022-12-10 um 12.40.30.png
    212,6 KB · Aufrufe: 6
  • Bildschirm­foto 2022-12-10 um 12.56.11.png
    Bildschirm­foto 2022-12-10 um 12.56.11.png
    440,2 KB · Aufrufe: 2
  • Bildschirm­foto 2022-12-10 um 13.05.42.png
    Bildschirm­foto 2022-12-10 um 13.05.42.png
    313 KB · Aufrufe: 1
  • memLeakTestView.hprof.zip
    memLeakTestView.hprof.zip
    1,9 MB · Aufrufe: 0
  • memLeakTest.zip
    memLeakTest.zip
    10 KB · Aufrufe: 0
Zuletzt bearbeitet:
Object ist die Oberklasse aller Java-Klassen, daher nicht so ganz verwunderlich.

Ich sehe da noch kein Speicherleck. Java verwaltet seinen Speicher selber und gibt ihn irgendwann, wenn der Garbage Collector der Ansicht ist, es sei nun Zeit, frei.

Dementsprechend wächst in der Regel der Speicherverbrauch an, weil die Objekte - obwohl möglich - nicht sofort weggeworfen werden, sondern erst irgendwann.
 
Hallo LimDul,
vielen Dank für Deine Antwort. Wenn dem so ist, dann OK. Aber klingt schon etwas strength das er so lange wartet um den 'Müll' raus zu bringen.
 
warum sollte der garbage collector was tun wenn es noch nicht nötig ist etwas zu tun

wenn du jeden tag um 7 den müll raus bringst aber der mülleimer fast leer ist dann lässt du es halt gut sein für den tag
 
Hallo LimDul,
vielen Dank für Deine Antwort. Wenn dem so ist, dann OK. Aber klingt schon etwas strength das er so lange wartet um den 'Müll' raus zu bringen.
Nun grundsätzlich gibt Java den Speicher nicht an das OS zurück. Ergo würde ein Aufruf vom Garbage Collector nix bringen, außer Performance zu kosten. (Solange noch genug Speicher da ist, den sich die JVM krallen kann)
 
warum sollte der garbage collector was tun wenn es noch nicht nötig ist etwas zu tun

wenn du jeden tag um 7 den müll raus bringst aber der mülleimer fast leer ist dann lässt du es halt gut sein für den tag

Ok, verstanden. Kann ich die Grösse des GC beeinflussen bzw sollte man es? Ich hab mich gestern Abend noch etwas belesen und bin über VisualVM gestoßen.
Mit den zusätzlichen Argumenten -Xmx12m -Xms3m -Xmn1m -XX:+UseSerialGC kann man die Heapgröße beeinflussen und man sieht dann in VisualVM wie der GC arbeitet. Standardmässig ist der Heap auf 250Mb einstellt (zumin was ich in ViusalVm gesehen habe). Dann erklärt sich natürlich der Anstiegt des Speicher und warum nichts freigegeben wird.

Die Frage die ich mir stelle. Sollte man die Heapgröße anpassen um eben nicht nicht bei einen kleinen Tool die vollen 250MB auszunutzen oder fällt es in die Kategorie, schei.. drauf wir haben doch genug Arbeitsspeicher? Für mein Empfinden ist das kein sauberer Weg. Ich Persönlich würde es reduzieren um eben nicht so viel Müll mit rum zu schleifen. Oder wie siehst Du / Ihr das? Könnt ihr mir ein Buch / Lektüre empfehlen wo man sowas beleuchtet?

Daraus resultierend, gibt es einen schönen Weg Memory Leaks im eigenen Programm zu finden bzw. zu prüfen ob mein Programm sauber läuft? Ich tue mich hier etwas schwer die ganzen Java Klassen durch zu ackern um irgendwo vielleicht eine Referenz zu meinen Klassen zu finden.
Ich hatte mir hierzu diese Video angesehen.
In min 2:40min erklärt er das man den Verlauf folgen soll um eine Referenz zu eigenen Klassen zu finden welche dann wiederum ein Strong Referenz zu den darüberliegende Objekten hält und somit nicht in den GC wandert. Soweit versteh ich das, was ich aber nicht verstehe.
Wenn wenn ich keine meiner Klassen darin befinden, sondern nur Java eigene (siehe Anhang), handelt es sich dann nicht auch um ein Leak? oder sind das gewollte Starke Bindungen welche nicht in den GC wandern sollen. Ich bin etwas verwirrt und würde mich dazu gern etwas einlesen.
Könnt ihr mir hier was empfehlen?



1000 Dank.
 

Anhänge

  • alles java.png
    alles java.png
    178,3 KB · Aufrufe: 2
Zuletzt bearbeitet:
Ok, verstanden. Kann ich die Grösse des GC beeinflussen bzw sollte man es?
Also was man einstellen kann, ist die Größe des Speichers, den die JVM maximal verwenden soll. Dazu kann man einen minimalen Speicherplatz und einen maximalen Speicherplatz angeben.

In den GC sollte man nicht eingreifen, so man nicht genau weiss, was man da macht. Das fällt in die Kategorie Optimierung und da ist die klare Regel: Don't! Also Finger weg von Optimierungen. (Oder als lange Antwort wäre es: Do not do it now! Wenn man Optimieren muss, dann muss man erst genau analysieren, wo und wie man da sinnvoll eingreifen kann.)

Und so wie Java Code bei Optimierungen generell: Das Ergebnis ist in der Regel nicht: "Ich optimiere meinen Code so, dass er etwas performanter ist". Wenn notwendig, sind das in der Regel veränderte Algorithmen. Daten werden anders vorgehalten so dass der Zugriff in einer anderen Laufzeit-Kategorie erfolgt.

Bei den GC ist das ähnlich. Denn es gibt nicht DEN GC. Es gibt mehrere Implementationen und wenn das wirklich ein Problem gibt, dann wählt man oft auch einen anderen GC. Und passt dann ggf seinen Code speziell für diesen GC an.

Einen ersten Überblick bekommt man z.B. auf https://www.javatpoint.com/types-of-garbage-collector-in-java oder https://www.baeldung.com/jvm-garbage-collectors - aber das sind keine kompletten Listen.

Und um die Komplexität nicht zu gering zu halten: Es gibt neben dem GC noch deutlich mehr Möglichkeiten. Sei es abweichende JVM Implementationen (J9 von IBM / Eclipse Foundation fällt mir da direkt ein) hin zu einer native Übersetzung (GraalVM).


Also was macht Sinn?
Meiner Meinung nach: Festlegung des benötigten Speichers / maximalen Speichers macht durchaus Sinn, so es die Umgebung erfordert. Auf meinem Arbeitsplatzrechner ist es schlicht egal. Aber auf einem produktiven Server wird es wichtig: Man will nicht, dass eine Applikation andere behindert weil es sich allen Speicher nimmt. Aber man will in Produktion lieber etwas mehr Hauptspeicher geben als dass ein Dienst plötzlich wegen einer OutOfMemoryException abstürzt.

Irgend welches "Herumpfuschen" am GC macht keinen Sinn. Die Entwickler haben mehr Wissen und optimieren einiges. Da ist die Gefahr groß, dass man beim Versuch etwas zu optimieren eher Probleme schafft.
 
Vielen Dank KonradN für deine Ausführlichen Input.
Das Tool was ich gebaut habe soll in der Tat auf einem Server rumduckel'n und am besten nur den Speicher nehmen den es braucht. Die Begrenzung des Speicher kann ich doch mit -Xmx20m -Xms5m festlegen, richtig?
 
Wenn das Programm auf einem Server laufen soll, dann sollte Xms und Xmx den gleichen Wert haben. Natürlich dann der Obergrenze entsprechend, bspw.: -Xms3584m -Xmx3584m. Es muss sowieso der der maximale Speicher verfügbar sein, dann wäre es ja dumm den nicht zu nutzen. Es passiert sonst folgendes: App steht unter starker Last Speicher wird zu klein - jetzt muss trotz der starken Last der GC laufen, gleichzeitig wird Speicher vergrößert, newGenSpace und oldGenSpace müssen neu berechnet und umgebaut werden ... Das führt dann zu den berühmten Lags in Java. Hat der Speicher die volle Größe, dann muss nur ab und an der GC laufen.

-Xmx20m -Xms5m - ich bin da evtl. etwas verwöhnt aber was sollen denn das für Werte sein? Unter -Xmx3m startet noch nicht mal das berühmte "Hallo Welt". Das sind keine realen Werte für einen Server.
 
Servus Oneixee5,
danke für den Hinweis. Von der Seite betrachtet macht es in der Tat keinen Sinn den Speicher zu begrenzen. Wie im 1.Beitrag geschrieben hab ich mich gewundert warum der Speicher ansteigt. Ich vermutete ein MemLeak in meinem Programm. Selbst jetzt weiss ich noch nicht genau wie ich das Prüfen kann. Was benutzt Du zur memLeak ermittlung? Da bin ich wohl etwas verwöhnt, bei Xcode wird einem in Instrument ein memleak angezeigt und man kann gezielt nachgehen. Oder waren es RetainCycle… hm schon ein Stück her wo ich damit was gemacht hatte…

LG David
 
Naja wir verwenden Dynatrace und andere kommerzielle Software. Ich würde mir aber erst mal gar keine Sorgen machen, in Java baut sich der verwendete Speicher auf und ab, das ist ganz normal. Wenn du sicher sein willst, dann mache einen Test, bevor du in Produktion gehst. Du kannst z.B.: mit https://jmeter.apache.org/ ein paar Stunden/Tage/... auf deiner Anwendung Last erzeugen. Wenn die Anwendung stabil läuft ist alles i.O.. Dabei kannst du deine Anwendung mit z.B.: JConsole aus dem JDK überwachen. Im Hobbybereich sollte das ausreichen.
Sollte wirklich ein Problem bestehen, kann man die Speicherabbilder speichern und auswerten. Das ist aber etwas komplexer als nur ein paar statistische Werte zu prüfen. Du solltest dich auf den Seiten der einschlägigen Tools informieren. Bei YouTube bin ich immer sehr skeptisch.
Erster Punkt, den man nachschauen kann, ist ob z.B.: UI-Komponenten oder Listener irgend welchen Listen/Collections hinzugefügt werden und diese über lange Zeit bestehen bleiben. Ich denke das ist eine sehr häufiges Problem. Wenn man sauber, strukturiert und überlegt arbeitet, dann sind Speicherlecks ein ziemlich seltenes Problem in Java. Halbwissen und einfach mal Code aus irgend einem Beispiel kopieren sind die häufigsten Ursachen für solche Probleme. Auch wenn's blöd klingt, am wichtigsten ist: RTFM
 
This problem is common in Java. See, the fact is that usually many java.lang.Object instances that show up in the JProfiler doesn’t necessarily mean there's a memory leak. This is because all the Java classes inherit from Object, and this is expected. Also, you can see Java manages memory through its garbage collector, which will not immediately release the unused memory. So, this behavior can be considered normal to see memory usage increase slowly over time, particularly when the GC has not initiated. If memory does not keep growing indefinitely, then this is likely normal behaviour and need not be considered as memory leak. In order to understand this better, how Java’s memory management works, check the details explained in this blog, what is Java Garbage Collection? This can give you a better perspective on JVM's memory management.
 

Zurück
Oben