Speicherfreigabe bei Java



  • Hallo,

    wenn ich mir meine Java Programme anschaue kommt es mir immer so vor als ober der GC den Speicher nicht aufräumt.

    Das veranlaste mich zu einem Test.

    c++ mit MFC Programm
    --------------------
    SetDlgItemText(IDC_CMDSTART, "....");
    CString* str = new CString[12345678];
    
    Sleep(10000);
    delete[] str;
    SetDlgItemText(IDC_CMDSTART, "&Start");
    
    Java
    ----------------------------
    import java.awt.*;
    import java.awt.event.*;
    import javax.swing.*;
    
    public class speicher
    {
        public static void main(String[] args) {
            JMyFrame a= new JMyFrame();
        }
    }
    class JMyFrame
    extends JFrame
    implements ActionListener
    {
        JButton cmd = new JButton("Start");
        JMyFrame() {
            cmd.addActionListener(this);
            this.getContentPane().add(cmd);
    
            setBounds(100,100,200,200);
            setVisible(true);
        }
    
        public void actionPerformed(ActionEvent ev)  {
            System.out.println("...");
            String str[] = new String[12345678];
            try {
            Thread.sleep(10000);
        }catch(InterruptedException e) {}
    
            System.out.println("Start");
    
            System.gc();
        }
    }
    

    Speicheranzeige im XP TaskManager

    Vor dem "Start" drücken
    Während des Sleeps
    Nach dem Sleep

    C++
    Speicher | Virtueller
    1,504 | 292
    49,848 | 48,568
    1,521 | 292

    Java
    Speicher | Virtueller
    11,400 | 10,624
    59,796 | 59,060
    59,836 | 74,332

    so warum wird der Speicher nicht freigegeben???
    Selbst wenn ich das Programm eine ganze Weile laufen lasse wird der Speicher einfach nicht freigegeben.

    Kann mir das jemand erklären ???


  • Mod

    Die Speicher-Anzeige des Taskmanagers muss nicht mit dem tatsächlich benötigten Speicher übereinstimmen. ... i.A. wird die Zahl, die da angezeigt wird, falsch sein.

    ...minimier das Fenster mal und mach es dann wieder groß. Der Taskmanager sollte dann einen anderen Speicherverbrauch anzeigen. (...bei mir macht er das zumindest!)



  • Vielleicht versteh ich das Programm nur nicht ... aber nach dem sleep existiert doch noch eine Referenz auf den String, also wird er nicht collectet. An der Stelle wo System.gc() aufgerufen wird genauso.


  • Mod

    Das "sleep" sollte tatsächlich rausgenommen werden, da unnütz. Der GC kann erst aktiv werden, wenn actionPerformed verlassen wird, da erst da keine Referenz auf das Array mehr existiert.

    ...zudem: AFAIK wird der GC normalerweise aktiv, wenn neuer Speicher benötigt wird und der Heap voll ist. ...oder wenn System.gc() aufgerufen wird, wobei hier eine Aktivierung des GC nicht garantiert ist. Das wird von Sun nur "empfohlen".

    EDIT: ...habe gerade gesehen, dass das sleep für etwas anderes gedacht ist. ...ist also doch nicht unnütz.

    Bashar: Das sleep ist da, damit er Zeit hat, im Taskmanager den Speicherverbrauch mit dem Array abzulesen.

    [ Dieser Beitrag wurde am 01.05.2003 um 19:40 Uhr von Gregor editiert. ]


  • Mod

    Du kannst den Code in actionPerformed mal durch folgenden ersetzen:

    [java]
    Runtime runtime = Runtime.getRuntime();
    System.out.println (runtime.totalMemory() - runtime.freeMemory());
    String [] array = new String [12345678];
    System.out.println (runtime.totalMemory() - runtime.freeMemory());
    array = null;
    System.gc();
    System.out.println (runtime.totalMemory() - runtime.freeMemory());[/code]

    ...und sag mal, was da rauskommt!



  • Ausgabe in der Konsole nach 3 Mal starten
    957760
    49975600
    593128

    ---------------
    600656
    49983672
    592208

    ---------------
    599632
    49930664
    548008

    ---------------

    Anzeigen im TaskManager (etwa):
    59000 | 62000
    55000 | 57000
    12000 | 11000 <-- hier ist er weg!!!

    @ Bashar war echt bescheuert von mir gc noch in der gleichen Methode aufzurufen.

    OK Programm leicht verändert:

    boolean b = false;
    
        public void actionPerformed(ActionEvent ev)  {
            b = !b;
            if (b) {
            System.out.println("...");
            String str[] = new String[12345678];
            try {
            Thread.sleep(10000);
        }catch(InterruptedException e) {}
            System.out.println("Start");
        } else {
    
            System.gc();System.gc();System.gc();System.gc();System.gc();
        }
        }
    

    Und siehe da! wenn ich einmal den gc aufrufe ist es das gleiche, aber mit den 5 Aufrufen wird der Speicher gleich freigegeben. Gewalt hilft wohl.

    Anscheinend Regelt der GC das lieber selber. Ok ich habe 256 MB Ram deswegen juckt ihn das nicht groß weil noch viel frei ist.
    So würde ich das jetzt interpretieren.



  • Der gc arbeitet sowieso nur nach internen Vorgaben. Der Aufruf von System.gc ist so wie ein kleiner Schubs "na! könntest du nicht mal ...?" auf die er dann reagieren kann oder auch nicht (wenn nicht nötig).


Anmelden zum Antworten