# Memory Leak mit BufferedImage !!



## 2crank (3. Okt 2009)

Hallo zusammen,


```
@Override
	public void actionPerformed(ActionEvent e) {
		
		Object o = e.getSource();
		
		if ( o == delete ) {
			staticScreenImg.flush();
			staticScreenImg = null;
			System.gc();
		}
		else if ( o == create ) {
			try {
				// 390 x 5286
				staticScreenImg = ImageIO.read(new File("D://CODEZ//Java//IndexCardProgram//staticImage.png"));
			}
			catch (IOException e1) {
				e1.printStackTrace();
			}
		}
	}
```

Nach dem erzeugen des BufferedImage steigt der verbrauchte RAM von ca 9.532 auf ca. 18.340.

Problem: Wenn ich das BufferedImage mit flush und der Zuweisung von null wieder aus dem Speicher haben will passiert nichts, der Speicher bleibt auf 18.340. Ich bin absolut verzweifelt und weiss nichtmehr weiter. Bitte helft mir


----------



## madboy (3. Okt 2009)

Meines Wissens ist nicht garantiert, wann und ob überhaupt der GarbageCollector aufräumt und Speicher wieder frei gibt. 
Wenn du kein wirkliches Problem mit dem Speicher (wie z.B. OutOfMemoryExceptions o.ä.) hast, würde ich das einfach ignorieren. 

Wie schaust du nach dem Speicherverbrauch? Glaube schon mal gehört zu haben, dass der Taskmanager von Windows nicht immer korrekte Werte anzeigt.


----------



## 2crank (3. Okt 2009)

Ja ich schau mit dem TaskManager.


----------



## hdi (3. Okt 2009)

Eig. sollte der Speicher frei gemacht worden sein. System.gc() ist zwar in der Tat nur eine "Bitte", keine Garantie, aber in der Praxis tut er's halt doch immer (zumindest war es bei mir immer so). Vgl. repaint() und co.

Ich würde mal java-intern überprüfen, wie es mit dem Speicher aussieht, weil ja madboy meinte der TaskManager zeigt das vllt nicht richtig an.

Wie war das nochmal, ich glaube die Klasse Runtime, bzw die Singleton Instanz, hat dafür Methoden, um den Speicherbedarf der JVM anzuzeigen.


----------



## 2crank (3. Okt 2009)

Ja bei der Runtime Klasse scheint er frei gemacht worden zu sein. Danke für die Hilfe.


----------



## musiKk (3. Okt 2009)

Den Speicherbedarf mit Betriebssystemmitteln zu prüfen, ist nicht zweckmäßig. Die Instanz einer JVM alloziert dynamisch bis zu einer Maximalgröße Speicher, wenn sie ihn benötigt. Wird ein großes Objekt wieder freigegeben, ist es nicht unbedingt sinnvoll, den Speicher der JVM wieder zu verringern (ich weiß nicht, ob sie das überhaupt jemals tut). Immerhin ist das Allozieren von Speicher eine vergleichsweise teure Angelegenheit.

Wenn Du wissen willst, wie die JVM den Speicher selbst nutzt, so sei Dir VisualVM ans Herz gelegt. Dort kann der GC auch manuell angestoßen werden. Der Nutzen von System.gc() hingegen ist mir noch schleierhaft. Das benötigt man eigentlich nie.


----------



## Noctarius (4. Okt 2009)

musiKk hat gesagt.:


> Den Speicherbedarf mit Betriebssystemmitteln zu prüfen, ist nicht zweckmäßig. Die Instanz einer JVM alloziert dynamisch bis zu einer Maximalgröße Speicher, wenn sie ihn benötigt. Wird ein großes Objekt wieder freigegeben, ist es nicht unbedingt sinnvoll, den Speicher der JVM wieder zu verringern (ich weiß nicht, ob sie das überhaupt jemals tut). Immerhin ist das Allozieren von Speicher eine vergleichsweise teure Angelegenheit.



Nein tut sie nicht. Die JVM vergrößert den Speicherbereich bis -Xmx (also max Speicher) aber gibt niemals wieder welchen frei in der Annahme, dass ein Programm nicht nur einmal Unmengen an Speicher benötigt sondern die selbe Operation schon öfter auftreten wird.


----------



## musiKk (4. Okt 2009)

Joah, ich meine das auch im Hinterkopf gehabt zu haben, war mir aber nicht mehr sicher. Klingt aus o. a. Grund imho auch nach einer sinnvollen Strategie.


----------



## Guybrush Threepwood (21. Jan 2011)

Ist ein älterer Thread, aber ich stehe gerade vor dem selben Problem. Der Speicher läuft mit der Zeit voll und und irgendwann fliegt eine OutOfMemory. Bei der Verwendung von BufferedImages mit Alpha-Kanal gibt es mit der Zeit auf manchen Rechnern einen fatalen Fehler und die ganze JVM crasht. Das ist bei mir auf einem etwas älteren Rechner mit WinXP der Fall (ich habe mir heute den ganzen Tag die Zähne ausgebissen). Das ist wirklich ein sehr unerfreuliches Problem, da ich in einer Anwendung mit vielen großen Bildern mit Transparenz arbeite. Insbesondere die Bilder mit Transparenz scheinen dabei ein besonderes Problem darstellen, aber auch BufferedImages ohne Alpha-Kanal. 

Offensichtlich ist ein Memory-Leak in der Graphik-Pipeline und das Video-RAM ist irgendwann erschöpft. Wenn man die Hardware-Beschleunigung abschaltet, dann crasht zumindest die JVM nicht mehr, aber der Speicher läuft dennoch voll. Auch wenn man wirklich alle Bilder nach Gebrauch "flusht", bleibt das Problem bestehen. Es gibt dazu Einträge in der Bug-Datenbank bei Oracle.


----------



## Guybrush Threepwood (21. Jan 2011)

Das ist wirklich ein verwirrender Fehler, siehe auch java - BufferedImage.getGraphics() resulting in memory leak, is there a fix? - Stack Overflow


----------



## Noctarius (21. Jan 2011)

Wenn du einen OutOfMemory bekommst dann kann der GC den Speicher nicht mehr freigeräumen weil du irgendwo einen Memory-Leak hast. Dies passiert oft durch vergessene Referenzen in Lists / Maps / Arrays.


----------



## Guybrush Threepwood (21. Jan 2011)

Hi Noctarius,
der Speicher läuft bei der Verwendung von BufferedImages mit Alpha-Kanal voll, selbst wenn man alle Referenzen explizit aufräumt, alle Bilddaten mit flush() freigibt etc. Das ist ein Bug in der JVM (siehe Stack Overflow-Eintrag), und er ist deshalb eine blöde Sache, weil Grafikdaten einfach sehr viel Speicher in Anspruch nehmen. Sonst läuft meine Anwendung völlig tadellos und fühlt sich auch sehr flüssig an. 
Es scheint sich wirklich um ein prinzipielles Problem zu handeln, (siehe z. B. auch Bug ID: 6716560 BufferedImage scaling leaks memory )


----------



## maki (22. Jan 2011)

Der Bug Report bezieht sich auf das JDK 1.5.0_12, hast du das Problem auhc mit einer aktuellen JRE/JDK?


----------



## Guybrush Threepwood (22. Jan 2011)

Ja, leider. Ich verwende 1.6.0_23. Mit der Option -Dsun.java2d.d3d=false crasht die JVM zumindest nicht mehr, aber der Speicher läuft trotzdem voll, obwohl explizit mit flush (BufferedImage), bzw. dispose (Graphics2D) alle zugehörigen Ressourcen freigegeben werden, und auch sonst keine Referenzen existieren.


----------



## Guybrush Threepwood (23. Jan 2011)

Nachtrag: Das Problem scheint wohl dann aufzutreten, wenn man bei einem BufferedImage mit Alpha-Kanal die createGraphics-Methode aufruft. Ohne Alpha-Kanal ist es offensichtlich kein Problem. Den Crash konnte ich auf Windows Vista und Windows 7 nicht replizieren. Das ist vermutlich ein Problem, das bei WinXP auftritt, möglicherweise in Verbindung mit dem Grafikkartentreiber.
Der Speicherverbrauch sinkt darüber hinaus, wenn man nach dem Flush zusätzlich das Image-Objekt auf null setzt.


----------



## Noctarius (23. Jan 2011)

Guybrush Threepwood hat gesagt.:


> Der Speicherverbrauch sinkt darüber hinaus, wenn man nach dem Flush zusätzlich das Image-Objekt auf null setzt.



Dann nutzt BufferedImage vermutlich DirectMapped Memory.


----------

