# BufferedImage vs. Heap Space ‒ Warum wird der Speicher nicht freigegeben?



## qwertz777 (8. Jun 2012)

Nehmen wir einmal an, ein Anfänger möchte Bildverarbeitung in Java realisieren und baut sich, zur Weiterverarbeitung, eine ArrayList mit Objekten eines Typs "Bild".

Die Klasse "Bild" erbt von "BufferedImage", führt ein paar Berechnungen durch und speichert die Ergebnisse in Klassenvariablen.
Die Information der einzelnen Pixel wird also am Ende nicht benötigt.

Problem ist nun, dass ich die Pixelinformation, die "BufferedImage" zwischenspeichert nicht wieder wegbekomme. flush() sollte evtl. tun, was ich will, nur tut es das nicht.
Resultat: sehr viel Arbeitsspeicher wird beansprucht ‒ Heap Space ist schnell voll.

Gibt es eine einfache Lösung, das Ding dazu zu bringen, dass nur noch die Werte der Klassenvariablen und nicht sämtliche Pixelinformation erhalten bleibt?

Es folgt der hoffentlich entscheidende Code-Schnipsel:

```
/**
	 * Generiert Thumbnail, führt Berechnungen durch.
	 * 
	 * @param picture
	 *            ein ganzes Bild
	 * @param filename
	 *            Dateiname
	 * @param calcRGB
	 *            Berechnungsart
	 */
	public Bild(BufferedImage picture, String filename, boolean calcRGBmax) {
		super(picture.getColorModel(), picture.copyData(null), picture
				.isAlphaPremultiplied(), null);

		this.filename = filename;

		int aspectRatio = (int) (getWidth() / getHeight() + 0.5);

		// skalieren und Thumbnail generieren
		if (aspectRatio < 4) {
			thumbnail = getScaledInstance(-1, 25,
					BufferedImage.SCALE_SMOOTH);
		} else {
			thumbnail = getScaledInstance(100, -1,
					BufferedImage.SCALE_SMOOTH);
		}

		(...)

		flush(); // tut nichts
	}
```

Dankeschön schon an dieser Stelle!

PS: Gibt es vielleicht eine simple Möglichkeit, zu schauen, wer/was den Arbeitsspeicher wie stark belegt?


----------



## Marco13 (8. Jun 2012)

Spontan erinnerte mich das an Bug ID: 6716560 BufferedImage scaling leaks memory , aber hab's nicht im Detail verglichen, kannst ja mal schauen. Ansonsten wären mehr Infos nicht verkehrt. Um wie viele/wie große Bilder geht es? MUSS man von BufferedImage erben? Wie groß ist der Heapsapce? (Also, ist es echt ein Memory leak, oder reicht's schon die Anwendung mit
java -Xmx1000m DieAnwendung
zu starten?)


----------



## SlaterB (8. Jun 2012)

zur Info 



> public void flush()
> 
> Flushes all reconstructable resources being used by this Image object. This includes any pixel data that is being cached for rendering to the screen as well as any system resources that are being used to store data or pixels for the image if they can be recreated. The image is reset to a state similar to when it was first created so that if it is again rendered, the image data will have to be recreated or fetched again from its source.
> 
> ...


----------



## qwertz777 (8. Jun 2012)

Ihr seid schnell!! :toll:



Marco13 hat gesagt.:


> Spontan erinnerte mich das an Bug ID: 6716560 BufferedImage scaling leaks memory , aber hab's nicht im Detail verglichen, kannst ja mal schauen.




```
public Bild(BufferedImage picture, String filename, boolean calcRGBmax) {
        super(picture.getColorModel(), picture.copyData(null), picture
                .isAlphaPremultiplied(), null);
 
        this.filename = filename;
 
        flush(); // tut nichts
    }
```
Verkürzt auf das hier bringt's keine Besserung. Somit ist das Skalieren nicht das Problem.




Marco13 hat gesagt.:


> Ansonsten wären mehr Infos nicht verkehrt. Um wie viele/wie große Bilder geht es?


Unter Ubuntu (Heap Space: ~1 GB) sind etwa 15 15-Megapixel-RGB-Bilder drin ‒ dann ist Ende.
Unter Windows 7 (Heap Space: ~250 MB) ist nur etwa ein 15-Megapixel-RGB-Bild möglich.



Marco13 hat gesagt.:


> MUSS man von BufferedImage erben?


Wäre (soweit ich das verstanden habe) das Einfachste, da ich lieber auf Pixel-Ebene herumrechnen würde, anstatt mich noch um das Einlesen und Parsen der Dateien zu kümmern.



Marco13 hat gesagt.:


> Wie groß ist der Heapsapce? (Also, ist es echt ein Memory leak, oder reicht's schon die Anwendung mit java -Xmx1000m DieAnwendung zu starten?)


~1 GB

@SlaterB: 





> the image data will have to be recreated or fetched again from its source.


 Daraus habe ich jetzt gelesen, dass eigentlich alles, was zwischengespeichert wurde flöten geht. ???:L
Was ist denn das "primary Raster"?


----------



## SlaterB (8. Jun 2012)

für genaue verlässliche Betrachtung bin ich der falsche, 
aber ein BufferedImage ist nunmal ein anderes Image als andere Arten, 
hat in meinen Augen das Array und die Pixelsicht als Grundlage im Objekt eingebaut (was auch immer die Quelle sein mag), 

während andere vielleicht die Jpeg-Komprimierung (mathematische Funktionen) beibehalten und nur temporär Pixel für die Anzeige ausrechnen (welche von flush() gelöscht werden kann?) und dafür kein setPixel() erlauben usw.


----------



## Spacerat (9. Jun 2012)

Das primäre Raster ist der Teil des Images, der sich nicht löschen lässt, sprich; Der Buffer des BufferedImage, welcher erst mit der Finalisierung (Zerstörung) des Bildobjektes stirbt (Deswegen heisst's BufferedImage... es hat halt immer einen Buffer ).
Für deinen Fall wäre es also nicht empfehlenswert BufferedImage zu erweitern und statt dessen RenderedImage zu implementieren.
Die Methoden des RenderedImage delegierst du dann an ein Instanz-BufferedImage, welches du löschen kannst, wenn du nur noch die anderen Instanzvariablen brauchst.


----------



## qwertz777 (13. Jun 2012)

@Spacerat: Hast mich in die richtige Richtung geschubst. 

Ich habe nun etwas aufgeräumt: mit einem BufferedImage in nun nur noch lokal innerhalb einer Methode gearbeitet.
... und nichts läuft über!

Schönen Dank euch allen!

EDIT: Das Problem mit dem Skalieren besteht übrigens weiterhin. Mal schauen, wie ich das umgehe ...


----------

