Speicherverwaltung

Status
Nicht offen für weitere Antworten.

Maverick85

Mitglied
Hallo,

ich habe eine Frage zur Speicherverwaltung. Ich entwickle normalerweise mit Objective-C und bin es gewohnt Objekte von Hand zu releasen. In Java blicke ich nicht ganz durch. Mir kommt die GC sehr willkürlich vor.

Ich schriebe gerade ein Programm das eine Klasse besitzt welche zB ein 2000x2000 Pixel großes BufferedImage erstellt (4Byte Typ wegen Transparenz).

Von der Klasse existieren sagen wir mal 10 Instanzen. In einer for Schleife rufe ich jede dieser Instanzen auf und speichere das erzeugte Bild als PNG ab.

Nun ist aber regelmäßig nach dem 3 manchmal auch 4 Bild der Speicher voll und das Programm bricht den Export Vorgang ab. Wenn ich den Heap Speicher erhöhe geht es wieder nur ist das Problem ja grundsätzlich nicht gelöst.
Eigentlich müsste man doch die Möglichkeit haben:

for-Schleifen Durchgang 1: Bild erzeugen - Bild speichern - Bild aus dem Speicher löschen
for-Schleifen Durchgang 2: Bild erzeugen - Bild speichern - Bild aus dem Speicher löschen

Somit könnte man beliebig viele Bilder hintereinander speichern. Denn die Bilder selbst sind alle gleich groß. Wenn eines erfolgreich abgespeichert wurde müssten ja auch alle anderen passen.

Momentan rufe ich in jedem for Schleifen Durchlauf zu Beginn System.gc() auf und am Ende setze ich das BufferedImage null. Was kann man noch tun?

Und ein weiteres Problem habe ich mit Threads, wenn ich die for Schleife in einem Thread ausführe komme ich noch viel schneller an Speichermaximum. Wie löscht man einen benutzten Thread vollständig aus dem speicher?

Danke für eventuelle Hilfe :)

Grüße Maverick85
 

Der Müde Joe

Top Contributor
Maverick85 hat gesagt.:
for-Schleifen Durchgang 1: Bild erzeugen - Bild speichern - Bild aus dem Speicher löschen
for-Schleifen Durchgang 2: Bild erzeugen - Bild speichern - Bild aus dem Speicher löschen

1 Referenz erzeugen:

for-schleife{

bild in referenz (nur EINE, immer dieselbe)
bild speichern

}

brauchst nur 1 Referenz und mit der Arbeitest du...oder hab ich jetzt was falsch verstanden?
(der GC macht das eigentlich alles alleine und ein System.gc(); brint keine garantie, dass Objekte
gelöscht werden)
 

Maverick85

Mitglied
So mach ich das ja habe mal etwas Pseudo Code:

Klassen Methode die das Image erzeugt:


Code:
public BufferedImage getImage()
{
	BufferedImage image = new BufferedImage(2000, 2000, BufferedImage.TYPE_4BYTE_ABGR);
		
	return image;
}

Hier ne Methode mit der for-Schleife:


Code:
public void saveImage()
{
	BufferedImage image;
		
	for (int index = 0; index < 10; index++)
	{
		...
		image = instanceX.getImage();
			
		imageWriter.write(null, new IIOImage(image, null, null), imageWriteParam);
			
		image = null;
	}
}

Ich hole mir ja immer da in der jeweiligen Klasse erzeugte Bild und speichere das danach setze ich null. Somit müsste ja keine harte Referenz mehr auf das Bild zeigen oder?

Aber trotzdem gibt es Speicherprobleme. Denke mal der GC leert sich nicht ständig aus.
Kann man eigentlich rausfinden wieviele harte Referenzen auf ein bestimmtes Objekt zeigen?
 

André Uhres

Top Contributor
Das Problem dürfte so nicht auftreten:
Code:
       new Thread(new Runnable() {

            public void run() {
                System.out.println("Start");
                for (int i = 0; i < 10000; i++) {
                    BufferedImage image = new BufferedImage(2000, 2000, BufferedImage.TYPE_4BYTE_ABGR);

                }
                System.out.println("Ende");

            }
        }).start();
Dieser Thread müsste in etwa 90 Sekunden problemlos zu Ende laufen,
und das ohne den Heap zu vergrössern. Man müsste also schon deinen ganzen Code analysieren.
Vielleicht machst du erstmal ein KSKB (siehe dazu 'Lies mich' hier links nebendran).
 

Der Müde Joe

Top Contributor
hab mal ein kleines Beispiel erstellt....null Probleme mit Heap
Code:
import java.awt.Graphics;
import java.awt.image.BufferedImage;
import java.io.File;
import java.io.IOException;

import javax.imageio.ImageIO;

public class Test {

	public static BufferedImage getImage() {
		return new BufferedImage(2000, 2000, BufferedImage.TYPE_4BYTE_ABGR);
	}

	public static BufferedImage fillImage(BufferedImage image) {
		Graphics g2d = image.getGraphics();
		g2d.fillRect(10, 10, 500, 500);
		return image;
	}

	/**
	 * 
	 * macht nur save...
	 * 
	 * @param image
	 * @param file
	 */
	public static void saveImage(BufferedImage image, File file) {
		try {
			ImageIO.write(image, "PNG", file);
			System.out.println("File: " + file.getName() + " written to:"
					+ file.getAbsolutePath());
		} catch (IOException e) {
			e.printStackTrace();
		}

	}

	public static void main(String[] args) {
		for (int i = 0; i < 5; i++) {
			// create image
			BufferedImage image = getImage();
			image = fillImage(image);

			saveImage(image, new File("blabla" + i + ".png"));
		}
	}

}
 

Maverick85

Mitglied
Der Müde Joe hat gesagt.:
hab mal ein kleines Beispiel erstellt....null Probleme mit Heap
Code:
import java.awt.Graphics;
import java.awt.image.BufferedImage;
import java.io.File;
import java.io.IOException;

import javax.imageio.ImageIO;

public class Test {

	public static BufferedImage getImage() {
		return new BufferedImage(2000, 2000, BufferedImage.TYPE_4BYTE_ABGR);
	}

	public static BufferedImage fillImage(BufferedImage image) {
		Graphics g2d = image.getGraphics();
		g2d.fillRect(10, 10, 500, 500);
		return image;
	}

	/**
	 * 
	 * macht nur save...
	 * 
	 * @param image
	 * @param file
	 */
	public static void saveImage(BufferedImage image, File file) {
		try {
			ImageIO.write(image, "PNG", file);
			System.out.println("File: " + file.getName() + " written to:"
					+ file.getAbsolutePath());
		} catch (IOException e) {
			e.printStackTrace();
		}

	}

	public static void main(String[] args) {
		for (int i = 0; i < 5; i++) {
			// create image
			BufferedImage image = getImage();
			image = fillImage(image);

			saveImage(image, new File("blabla" + i + ".png"));
		}
	}

}

Danke für die Mühe. Das werde ich gleich mal testen. Aber wahrscheinlich liegt das Problem dann doch tiefer. Das Programm ist schon recht komplex geworden. Werde mal nachher probieren ein KSKB anzufertigen das alles wesentliche abdeckt.
 

Maverick85

Mitglied
Hallo,

habe mal den Code reduziert. Wenn ich den unteren Code ausführe kriege ich nach dem ersten gespeicherten Bild schon ein Fehler:

Code:
0
1
Exception in thread "Thread-0" java.lang.OutOfMemoryError: Java heap space
	at java.awt.image.DataBufferByte.<init>(DataBufferByte.java:42)
	at java.awt.image.Raster.createInterleavedRaster(Raster.java:253)
	at java.awt.image.BufferedImage.<init>(BufferedImage.java:385)
	at Main$1.run(Main.java:55)
	at java.lang.Thread.run(Thread.java:613)

Wenn ich das System.gc() nicht auskommentiere schaffe ich 24 Bilder abzuspeichern danach gibt wieder ein Out of Memory.

Code:
import java.awt.Color;
import java.awt.Graphics2D;
import java.awt.image.BufferedImage;
import java.io.FileOutputStream;

import javax.imageio.IIOImage;
import javax.imageio.ImageIO;
import javax.imageio.ImageWriter;
import javax.imageio.stream.ImageOutputStream;
import javax.swing.JFileChooser;

public class Main
{
	public static void main(String[] args)
	{
		new Thread(new Runnable()
		{
			public void run()
			{
				try
				{
					JFileChooser selectFolderDialog = new JFileChooser();
					selectFolderDialog.setFileSelectionMode(JFileChooser.DIRECTORIES_ONLY);
					selectFolderDialog.setDialogTitle("Export");
					selectFolderDialog.setCurrentDirectory(null);
					selectFolderDialog.setMultiSelectionEnabled(false);
					
					int returnValue = selectFolderDialog.showOpenDialog(null);
					
					if (returnValue == JFileChooser.APPROVE_OPTION)
					{
						String directory = selectFolderDialog.getSelectedFile().getAbsolutePath();
						
						BufferedImage image;
						String path;
						FileOutputStream fileOutputStream;
						ImageOutputStream imageOutputStream;
						ImageWriter imageWriter;
						Graphics2D graphics;
						
						for (int index = 0; index < 40; index++)
						{
							System.out.println(index);
							
							//System.gc();
							
							path = directory + "/" + Integer.toString(index) + ".png";
							
							fileOutputStream = new FileOutputStream(path);
							imageOutputStream = ImageIO.createImageOutputStream(fileOutputStream);
							
							imageWriter = ImageIO.getImageWritersByFormatName("png").next();
							imageWriter.setOutput(imageOutputStream);
							
						    image = new BufferedImage(3000, 3000, BufferedImage.TYPE_4BYTE_ABGR);
						    graphics = image.createGraphics();
							graphics.setColor(Color.RED);
							graphics.fillRect(0, 0, 3000, 3000);
							
						    imageWriter.write(null, new IIOImage(image, null, null), null);
						    
						    graphics.dispose();
						    graphics = null;
						    
						    image.flush();
						    image = null;
						    
						    imageOutputStream.flush();
						    imageOutputStream.close();
						    imageOutputStream = null;
						    
						    fileOutputStream.flush();
						    fileOutputStream.close();
						    fileOutputStream = null;
						    
						    imageWriter.dispose();
						    imageWriter = null;
						    
						    path = null;
						}
					}
				}
				catch (Exception exception)
				{
					System.out.println("Exception");
				}
            }
        }).start();
	}
}
 

Der Müde Joe

Top Contributor
seh grad nix was ich beanstanden könnte.
Hab mal alles auf das essentielle Komprimiert...
Code:
import java.awt.Color;
import java.awt.Graphics2D;
import java.awt.image.BufferedImage;
import java.io.FileOutputStream;
import java.io.IOException;

import javax.imageio.ImageIO;
import javax.imageio.ImageWriter;
import javax.imageio.stream.ImageOutputStream;
import javax.swing.JFileChooser;

public class TT {

	public static BufferedImage createImage() {
		BufferedImage image = new BufferedImage(3000, 3000,
				BufferedImage.TYPE_4BYTE_ABGR);
		Graphics2D graphics = image.createGraphics();
		graphics.setColor(Color.RED);
		graphics.fillRect(0, 0, 3000, 3000);
		return image;
	}

	public static void main(String[] args) throws IOException {
		Runtime runtime = Runtime.getRuntime();

		JFileChooser selectFolderDialog = new JFileChooser();
		selectFolderDialog.setFileSelectionMode(JFileChooser.DIRECTORIES_ONLY);
		selectFolderDialog.setDialogTitle("Export");
		selectFolderDialog.setCurrentDirectory(null);
		selectFolderDialog.setMultiSelectionEnabled(false);

		int returnValue = selectFolderDialog.showOpenDialog(null);

		if (returnValue == JFileChooser.APPROVE_OPTION) {
			String directory = selectFolderDialog.getSelectedFile()
					.getAbsolutePath();

			System.out.println("Max: " + (runtime.maxMemory() / 1024)+ " KB");
			System.out.println("Total: " + (runtime.totalMemory() / 1024)+ " KB");
			for (int index = 0; index < 400; index++) {
				System.out.println(index + "--------------------------------");
				System.out.println("Free: " + (runtime.freeMemory() / 1024)+ " KB");

				String path = directory + "/" + index + ".png";

				FileOutputStream fileOutputStream = new FileOutputStream(path);
				ImageOutputStream imageOutputStream = ImageIO
						.createImageOutputStream(fileOutputStream);

				ImageWriter imageWriter = ImageIO.getImageWritersByFormatName(
						"png").next();
				imageWriter.setOutput(imageOutputStream);

				BufferedImage image = createImage();

				imageWriter.write(image);

				imageOutputStream.close();
				fileOutputStream.close();
			}
		}

	}
}


Max: 65088 KB
Total: 5056 KB
0--------------------------------
Free: 2981 KB
1--------------------------------
Free: 3258 KB
2--------------------------------
Free: 4079 KB
.....
Free: 3532 KB
41--------------------------------
Free: 3511 KB
42--------------------------------
Free: 3490 KB

Also es würde noch so weitergehen, hab dann mal abgebrochen....
(Ubuntu 7.10 java 1.6_03)

probier mal bei dir
 

Maverick85

Mitglied
Hi,

hab ich vergessen zusagen ich benutze Java 1.5.0_13-119 (Mac OS X Leopard).

Dein Code funktioniert. Ich musste nur den Max Heap Speicher auf 128 MB stellen.
Habe komischerweise einen höheren Speicherverbrauch. Liegt wohl an der Java OS X Implementation.

Code:
Max: 130112 KB
Total: 1984 KB
0--------------------------------
Free: 550 KB
1--------------------------------
Free: 806 KB
2--------------------------------
Free: 27318 KB
3--------------------------------
Free: 27244 KB
4--------------------------------
Free: 27381 KB
5--------------------------------
Free: 27346 KB
6--------------------------------
Free: 27219 KB

Den Befehl runtime.freeMemory() kannte ich noch gar nicht werde jetzt nochmal meinen Code überarbeiten. Und das beobachten.

Grüße

Maverick85
 
Status
Nicht offen für weitere Antworten.

Ähnliche Java Themen

Neue Themen


Oben