Wer kann mir das hier erklären? Programm frisst RAM!

xip

Bekanntes Mitglied
Hallo,

mein Programm frisst immer und immer mehr Speicher!

Java:
public class start {

	public static void main(String[] args) 
	{
		new start();
	}
	
	start()
	{
			for(int i = 0; i < 20 ; i++)
			{
				Thread obj_thread = new Thread(){
					public void run()
					{
						while(true)
						{
							String str_wert = this.getName(); // 
						}
					}
				};
				obj_thread.start();
			}
	}
}

doch das nun wirklich komische daran ist wenn ich das this.getName() welches einen normalen String zurückgibt gegen "hallo" oder sonst einen String austausche bleibt das Programm wie erwartet klein!! Habt ihr eine idee??


Java:
public class start {

	public static void main(String[] args) 
	{
		new start();
	}
	
	start()
	{
			for(int i = 0; i < 20 ; i++)
			{
				Thread obj_thread = new Thread(){
					public void run()
					{
						while(true)
						{
							String str_wert = "was soll das den???";
						}
					}
				};
				obj_thread.start();
			}
	}
}
 
getName() => return String.valueOf(name); => return new String(data);

es wird also jedes Mal ein neuer String erstellt.

Das andere ist eine "Konstante", die wird also im String-Pool abgelegt werden, d.h. du hast so immer nur diesen einen String !
 
[c]Thread.getName()[/c] erzeugt je Aufruf eine neue String-Instanz. Das Programm wird Speicher benutzen wie es ihn zur Verfügung bekommt. Irgendwann wird der Garbage-Collector die unbenutzten String-Instanzen wieder auflösen. Wann das passiert, weiß die JVM allein.

Probier mal so:
Java:
public class Start {
 
    public static void main(String[] args) 
    {
        new Start();
    }
    
    Start()
    {
            for(int i = 0; i < 20 ; i++)
            {
                Thread objThread = new Thread(){
                    public void run()
                    {
                        for(int i = 0; ; i++)
                        {
                            String strWert = this.getName(); //
                            if ((i % 1000) == 0) {
                              System.gc();
                            }
                        }
                    }
                };
                objThread.start();
            }
    }
}

Ebenius

PS: KlassenNamen und variablenNamen angepasst.
 
Ein explizites antriggern des GC ist in der Regel sinnfrei. Die VM weiß am Besten, wann sie Speicher wie freigeben sollte. Diese Algorithmen sind bedingt konfigurierbar, was auch der Ansatzpunkt für Anpassungen an die eigenen Bedürfnisse sein sollte. Ein Eingriff über den Code wird in der Regel zu einer schlechtern Performance führen, schwer oder gar nicht mehr zu optimieren.
 
Da stimme ich zu. Ich habe den Aufruf unten nur zum Test eingebaut, damit man sehen kann, dass kein wirkliches Leck besteht.

Ebenius
 
Hi xip,

Dein Quelltext ist ein gutes Beispiel dafür, wie man mit Threads nicht umgehen sollte... :noe:

Leider kann ich nicht mal erahnen, was Dein Programm überhaupt machen soll.
Soll es einfach die Namen der gestarteten Threads ausgeben? Na ja, egal.

Zwei Grundsätze:

1.) Man deklariert keine Variablen IN einer FOR-Schleife sondern DAVOR.
Mit jedem Durchlauf der Schleife wird sonst eine neue Instanz der Variablen erzeugt. Das kostet Arbeitsspeicher!

2.) In der run() Methode niemals while(true) verwenden da:
a.) Die Prozessorlast dann bei 100% liegt
b.) Sich der Thread nicht korrekt beenden lässt


Hier ein Beispiel wie man mit Threads richtig umgeht (und natürlich frisst das kein RAM...):

Java:
   public static void main(String[] args) {
        ThreadGroup threadGroup = new ThreadGroup("tg");
        Thread objThread;
        for (int i = 0; i < 20; ++i) {
            objThread = new Thread(threadGroup, "thread_" + i) {
                public void run() {
                    while (!isInterrupted()) { //nicht while(true)...
                        try {
                            //Macht irgendwas...
                            System.out.println("Hier Thread \"" + getName() + "\". Ich mache irgendwas...");
                            sleep(1000);
                        } catch (InterruptedException e) {
                            System.out.println("Thread \"" + getName() + "\" wird beendet!");
                            return; //Und raus aus der run() Methode...
                        }
                    }
                }
            };
            objThread.start();
        }

        //Nun machen wir mal nix und lassen die 20 Threads 10 Sekunden laufen...
        try {
            Thread.sleep(10000);
        } catch (InterruptedException ignore) {}

        //Nun durchlaufen wir die ThreadGroup und lassen uns den Namen der Threads ausgeben
        //und beenden sie anschliessend...
        Thread[] objThreads = new Thread[threadGroup.activeCount()];
        threadGroup.enumerate(objThreads);
        for (Thread thread : objThreads) {
            System.out.println("Threadname: " + thread.getName());
            //So beendet/unterbricht man einen Thread korrekt
            thread.interrupt();
        }
  }

Ich hoffe das hilft...

Gruß, aumaster

P.S.: Ein Aufruf von System.gc() oder Runtime.getRuntime().gc() sollte man besser lassen.
Das hat meist die gegenteilige Wirkung und frisst oft noch mehr RAM anstatt was freizugegeben...
 
Zuletzt bearbeitet:
Dein Quelltext ist ein gutes Beispiel dafür, wie man mit Threads nicht umgehen sollte... :noe:
Der Quelltext ist ein Testprogramm. Natürlich ergibt es keinen weiteren Sinn. :bahnhof:

1.) Man deklariert keine Variablen IN einer FOR-Schleife sondern DAVOR.
Mit jedem Durchlauf der Schleife wird sonst eine neue Instanz der Variablen erzeugt. Das kostet Arbeitsspeicher!
Das ist Unsinn. Aus zwei Gründen:
  1. Man deklariert Variablen so knapp wie möglich. Wenn eine Variable außerhalb eines Blocks nicht benötigt wird, dann deklariert man sie auch nicht außerhalb.
  2. Ob die Variable außerhalb oder innerhalb der Schleife deklariert ist oder nicht hat mit der Speicherverwaltung genau nix zu tun. Die Instanzen werden aufgebaut, egal wo die Variable deklariert ist. Selbst wenn Du statt [c]String str_wert = this.getName(); //[/c] nur [c]this.getName(); //[/c] schreibst wirst Du das selbe Problem erfahren.

2.) In der run() Methode niemals while(true) verwenden da:
a.) Die Prozessorlast dann bei 100% liegt
b.) Sich der Thread nicht korrekt beenden lässt
Das ist ebenfalls Unsinn. Ein typischer, über Netzwerk erreichbarer Server macht genau das; er nimmt in einer [c]while(true)[/c]-Schleife Anfragen entgegen und arbeitet sie ab. Abgebrochen werden kann der Thread dann noch immer; entweder innerhalb der Schleife mit break oder per [c]interrupt()[/c] und zugehöriger InterruptedException. Ganz abgesehen davon, dass der Test oben ja absichtlich Last erzeugen soll!

Hier ein Beispiel wie man mit Threads richtig umgeht (und natürlich frisst das kein RAM...):[…]
Man leitet die Thread-Klasse nicht ab, nur um sie zu benutzen. Statt dessen übergibt man dem Thread ein Runnable das man implementiert. Die API-Doc des Runnable-Interfaces sagt dazu:
In most cases, the Runnable interface should be used if you are only planning to override the run() method and no other Thread methods. This is important because classes should not be subclassed unless the programmer intends on modifying or enhancing the fundamental behavior of the class.

Ebenius
 
Dieses mal stimme ich voll zu... oder fast voll.

Das ist ebenfalls Unsinn. Ein typischer, über Netzwerk erreichbarer Server macht genau das; er nimmt in einer while(true) -Schleife Anfragen entgegen und arbeitet sie ab. Abgebrochen werden kann der Thread dann noch immer; entweder innerhalb der Schleife mit break oder per interrupt() und zugehöriger InterruptedException. Ganz abgesehen davon, dass der Test oben ja absichtlich Last erzeugen soll!

while(true) ist zwar gängige Praxis, aber nicht die beste Lösung. Eine gute Lösung wäre, auf das Interrupted-Flag des Threads zu prüfen, denn eine InterruptedException wird ja nur empfangen, wenn der Thread gerade blockiert. Das wäre auch die beste Lösung einen Thread von außen zu beenden: ihn unterbrechen.
 
Ah, ich sehe schon - keine Lösung gepostet, aber den Lösungsversuch
von anderen bewusst nicht verstehen wollen und dann verreissen... :toll:
 
Ja genau. Das machen wir hier immer so. ;-)

Im Ernst: Meiner Meinung nach sollten Sätze nicht lauten "Niemals dies und das machen" wenn sie dann nicht unumstößlich sind. Dem Themeneröffner ging es im Übrigen eher daraum zu verstehen, wieso ein derartiges Programm -- auch wenn es effektiv keine Daten sammeln sollte -- trotzdem augenscheinlich immer mehr Speicher verbraucht. Und das hatte er beantwortet bekommen. Oder nicht?

Ebenius
 
Ah, ich sehe schon - keine Lösung gepostet, aber den Lösungsversuch
von anderen bewusst nicht verstehen wollen und dann verreissen... :toll:

Heul doch! ;(

Es geht darum, dass der Threadstarter und alle, die diesen Thread in Zukunft lesen keine Fehlinformationen als Wahrheit annehmen, deshalb die Korrekturen.
Ich poste selten Code, aber ich gebe Lösungen bzw. Lösungsansätze. Erwartet jemand Code, antworte ich in der Regel nicht.
Ich finde es gut, dass hier jeder auch mal suboptimale Lösungen bzw. Beiträge postet.Nobody is perfect. Das ist ein Diskussionsforum und die Postings dienen zur Diskussion, damit jeder lernen kann, wie man es am Besten machen sollte, auch jemand, der vermeintlich eine Lösung gepostet hat. Das mit den Diskussionen verstehen viele nur nicht. Sie gehen davon aus, dass es hier kostenlosen, persönlichen Support bekommt... eine Fehleinschätzung!
 

Zurück
Oben