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

qwertz777

Mitglied
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:
Java:
	/**
	 * 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?
 
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?)
 
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.

Examples of how this method affects specific types of Image object:

BufferedImage objects leave the primary Raster which stores their pixels untouched, but flush any information cached about those pixels such as copies uploaded to the display hardware for accelerated blits.
...
 
Ihr seid schnell!! :toll:

Spontan erinnerte mich das an Bug ID: 6716560 BufferedImage scaling leaks memory , aber hab's nicht im Detail verglichen, kannst ja mal schauen.
Java:
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.


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.

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.

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"?
 
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.
 
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.
 
@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 ...
 
Zuletzt bearbeitet:

Zurück
Oben