# Rotieren eines 2D Images endet in Java heap space Error



## just4fun (27. Jan 2009)

Guten Tag,
es geht um folgendes:
Ich habe eine 2000x2000 pixel map. Diese soll sich um 3° Drehen, wenn man die rechte Pfeiltaste drückt.
Das funktioniert folgendermaßen:
Beim init ist folgendes enthalten:

```
MediaTracker mt = new MediaTracker(this);
    mt.addImage(arena, 0);
    try {
        mt.waitForID(0);
    } 
    catch (InterruptedException ie) {
    }
```
Dieses komtm dann, wenn die map geladen ist:

```
Tarena = new BufferedImage(2000, 2000, BufferedImage.TYPE_INT_ARGB);
    Graphics2D g = (Graphics2D) Tarena.getGraphics();
    //arena ist das Image was auf die 2000x2000 px map verweist
    g.drawImage(arena, 0, 0, null);
```
Und dann passiert folgendes, wenn man auf die rechte bzw. linke Pfeiltaste drückt:

```
bio = new AffineTransformOp(AffineTransform.getRotateInstance(3.0* Math.PI / 180, Xchar, Ychar),AffineTransformOp.TYPE_NEAREST_NEIGHBOR); 
    Tarena = bio.filter(Tarena, null);
```


Danach wird das Bild halt gezeichnet, was auch funktioniert. Nach etwa sieben Drehungen aber, erscheint folgender Fehler: 
Exception in thread "Timer-0" java.lang.OutOfMemoryError: Java heap space
    at java.awt.image.DataBufferInt.<init>(DataBufferInt.java:41)
    at java.awt.image.SinglePixelPackedSampleModel.createDataBuffer(SinglePixelPackedSampleModel.java:220)
    at sun.awt.image.IntegerInterleavedRaster.<init>(IntegerInterleavedRaster.java:55)
    at sun.awt.image.IntegerInterleavedRaster.createCompatibleWritableRaster(IntegerInterleavedRaster.java:515)
    at java.awt.image.AffineTransformOp.createCompatibleDestImage(AffineTransformOp.java:448)
    at java.awt.image.AffineTransformOp.filter(AffineTransformOp.java:209)

Der Fehler passiert bei der Zeile:

```
Tarena = bio.filter(Tarena, null);
```


Was ist nun der Grund für den Fehler? Nach genau sieben Rotationen? Wird das alte Bild nicht aus dem Speicher gelöscht? Wenn ja, wie kann ich das rauslöschen? Ein System.gb() vor dem bio.filter hat nicht geholfen. Rotieren funktioniert mit einem kleineren Bild (z.B. 32*32 px) super(gibt keinen error). Ist die map einfach zu groß?


Danke für Eure Antworten!

Finn


----------



## Marco13 (27. Jan 2009)

Dass das nach "einigen" Schritten passiert, deutet eigentlich auf ein Memory Leak hin. I.a. kann man bei einem "java.lang.OutOfMemoryError: Java heap space" einfach sein Programm mit
java -Xmx256m MeinProgramm
starten. Falls da dann nach 20 oder 30 Schritten das gleiche Problem auftritt, hast du offenbar ein Memory Leak. Dann müßtest du compilierbren Code posten, bei dem das Problem auftritt.


----------



## manuche (27. Jan 2009)

Setz einfach mal deine Objekte die du nicht mehr brauchst am ende der Methode auf null und ruf den GarbageCollector auf...
Eigentlich sollte das zwar automatisch passieren aber man weiss ja nie!


----------



## Wolfgang Lenhard (27. Jan 2009)

Mache - wenn es nicht mehr gebraucht wird - ein g.dispose().


----------



## manuche (27. Jan 2009)

Ich gehe mal davon aus, dass das Drehen komplett in der keyPressed-Methode stattfindet, oder? Schreib am besten mal die ganze Methode damit man nachvollziehen kannst wie du mit den Objekten umherschmeisst dass es zu nem Stackoverflow kommt


----------



## just4fun (27. Jan 2009)

Danke für die schnellen Antworten!

Sollte das nicht auch funktionieren, ohne den Speicher zu erhöhen? Oder nur um rauszufinden ob es ein Memory Leak ist?

Und dazu:


			
				manuche hat gesagt.:
			
		

> Setz einfach mal deine Objekte die du nicht mehr brauchst am ende der Methode auf null und ruf den GarbageCollector auf...
> Eigentlich sollte das zwar automatisch passieren aber man weiss ja nie!



Ist das denn nötig? Das BufferedImage Tarena wird ja immer neu überschrieben, das muss man dann ja nicht auf null setzen?


Ich werde später mal den Code posten. 

Finn


----------



## Marco13 (27. Jan 2009)

just4fun hat gesagt.:
			
		

> Sollte das nicht auch funktionieren, ohne den Speicher zu erhöhen? Oder nur um rauszufinden ob es ein Memory Leak ist?


Zweimal ja.


----------



## just4fun (28. Jan 2009)

Okay, habe das mal probiert mit -Xmx256m. Ja, ich bekomme nun 21 Rotationen hin -> Also ist es ein Memory Leak. Hilft immer, wenn man weiß, wo das Problem liegt. Da haben wir schonmal einen Schritt geschafft.^^



Hier ist dann der Code:

```
import java.awt.*;
import java.awt.image.ImageObserver;
import java.awt.event.*;
import java.util.*;
import java.lang.*;
import java.io.*;
import java.awt.image.AffineTransformOp;
import java.awt.image.BufferedImage;
import java.awt.image.BufferedImageOp;
import java.awt.geom.AffineTransform;

public class RotationTest extends Frame implements KeyListener{
	
	//Dieses Image ist 2000x2000 px groß, und soll rotiert werden.
	Image map = Toolkit.getDefaultToolkit().getImage(getClass().getResource("map.gif"));
	
	//Nach jeder Rotation wird BackgroundImage = RotatedMap; gesetzt.
	Image BackgroundImage = map;
	
	BufferedImage RotatedMap;
		
	BufferedImageOp bio;
	
	double curAngle = 0.0;
	
	//RotationTest wird gestartet
    public static void main(String[] args)
    {
        RotationTest starter = new RotationTest();
        starter.show();
    }

    public RotationTest() {
		super("RotationTest");
		setBackground(Color.white);    	
    	setSize(700,500);
		setLocation(0,0);
		setVisible(true);
		addKeyListener(this);
      		
	    MediaTracker mt = new MediaTracker(this);
	    mt.addImage(map, 0);
	    try {
	    	mt.waitForID(0);
	    } 
	    catch (InterruptedException ie) {
	    }

		RotatedMap = new BufferedImage(2000, 2000, BufferedImage.TYPE_INT_ARGB);
		Graphics2D g = (Graphics2D) RotatedMap.getGraphics();
    	g.drawImage(map, 0, 0, null);
    	repaint();
    }
    
    public void keyPressed(KeyEvent e) {
    	int k = e.getKeyCode();
    	
    	//KeyCode 37 ist die linke Pfeiltaste. Das Bild wird um 3° nach links gedreht.
    	if(k == 37) {
			bio = new AffineTransformOp(AffineTransform.getRotateInstance(-3.0* Math.PI / 180, 0, 0),AffineTransformOp.TYPE_NEAREST_NEIGHBOR);  	
	    	RotatedMap = bio.filter(RotatedMap, null);
	    	BackgroundImage = RotatedMap;
	    	repaint();
	    	curAngle-=3.0;	    	
	    	System.out.println("Current Angle:" + curAngle);	    	
    	}
		//KeyCode 39 ist die rechte Pfeiltaste. Das Bild wird um 3° nach rechts gedreht.    	
     	if(k == 39) {
			bio = new AffineTransformOp(AffineTransform.getRotateInstance(3.0* Math.PI / 180, 0, 0),AffineTransformOp.TYPE_NEAREST_NEIGHBOR);  	
	    	RotatedMap = bio.filter(RotatedMap, null);
	    	BackgroundImage = RotatedMap;
	    	repaint();
	    	curAngle+=3.0;	    	
	    	System.out.println("Current Angle:" + curAngle);	    	
    	}   	
    }
    public void keyReleased(KeyEvent e) {
    }
    public void keyTyped(KeyEvent e) {
    }    
    	
	public void paint(Graphics g) {
		g.drawImage(BackgroundImage, -1000, -1000, this);
	}
}
```


Das einzige was man braucht um dies zu testen, ist ein 2000x2000px großes Bild benannt 'map.gif'(Am Besten nicht einfarbigen, sonst kann man den Rotationseffekt nicht sehen).

Nach einigen Rotationen stürzt das Programm dann ab.

Was noch besser wäre, wenn anstatt das schon veränderte Bild um immer 3° zu drehen, wäre, wenn man das Anfangsbild um curAngle - 3.0/curAngel + 3.0 drehen würde, weil das die Qualität verbessert. Dann aber stürzt das Program nach zwei Rotationen ab.


Finn


PS.:
Hier ist das Image map.gif, das ich benutzt habe(Auf das Bild klicken und dann rechts-Klick 'Speichern als...'):


----------



## manuche (28. Jan 2009)

Probier mal folgendes:

```
bio.filter(RotatedMap, rotatedMap);
```
statt

```
rotatedMap= bio.filter(RotatedMap, null);
```
habe das hier in der Doku gefunden:


> Performs a single-input/single-output operation on a BufferedImage. If the color models for the two images do not match, a color conversion into the destination color model is performed. If the destination image is null, a BufferedImage with an appropriate ColorModel is created.


Ist allerdings nur eine Vermutung da ich in dem Umfeld nicht allzuviel Erfahrung habe!

Noch was: Gewöhn dir ab Methoden und Variablen groß zu schreiben... In Java werden sie KLEIN geschrieben!


----------



## just4fun (28. Jan 2009)

Ne, mit

```
bio.filter(RotatedMap,rotatedMap)
```
bringt keine Verbesserung.

Danke aber auf jedenfall, dass du versuchst zu helfen.^^

Und der Tipp mit dem Kleinschreiben ist auf jedenfall etwas, dass ich jetzt auch weiß. Danke sehr!

Finn


----------



## manuche (28. Jan 2009)

Hat es eigentlich einen Grund warum du AffineTransformOp und BufferedImageOp bentutz?
Nicht das es einfach nur Overhead ist...


----------



## just4fun (28. Jan 2009)

Ne, habs halt vom folgenden Beispiel:
www.java2s.com/Code/Java/2D-Graphics-GUI/RotateImage45Degrees.htm

Finn


----------



## manuche (28. Jan 2009)

```
import java.awt.*; 
import java.awt.event.*;  
import java.io.*; 
import java.net.URL; 
import java.awt.image.BufferedImage; 
import java.awt.geom.AffineTransform; 

import javax.imageio.ImageIO;

public class RotationTest extends Frame implements KeyListener{ 
    
   BufferedImage map = this.loadImg("map.gif");
   BufferedImage backgroundImage = map;
    
   double curAngle = 0.0; 
    
    public static void main(String[] args) 
    { 
        RotationTest starter = new RotationTest(); 
        starter.show();
    } 

    public RotationTest() { 
      super("RotationTest"); 
      setBackground(Color.white);        
       setSize(700,500); 
      setLocation(0,0); 
      setVisible(true); 
      addKeyListener(this);  
    } 
    
    public void keyPressed(KeyEvent e) { 
       int k = e.getKeyCode(); 
       if (k == 37 || k == 39) { 
	       //KeyCode 37 ist die linke Pfeiltaste. Das Bild wird um 3° nach links gedreht. 
	       if(k == 37) { 
	          curAngle-=3.0;           
	          System.out.println("Current Angle:" + curAngle);           
	       } 
	       //KeyCode 39 ist die rechte Pfeiltaste. Das Bild wird um 3° nach rechts gedreht.        
	       if(k == 39) {
	          curAngle+=3.0;           
	          System.out.println("Current Angle:" + curAngle);           
	        }
	        BufferedImage tempImage = new BufferedImage (map.getWidth(), map.getHeight(), BufferedImage.TRANSLUCENT);
	        Graphics2D g2d = (Graphics2D) tempImage.getGraphics ();
	        AffineTransform aft = new AffineTransform ();
	        aft.rotate (Math.toRadians (curAngle), map.getWidth () / 2, map.getHeight () / 2);
	        g2d.drawImage (map, aft, null);
	        g2d.dispose();
	        backgroundImage = tempImage;
	        this.repaint();
       }
    } 
    public void keyReleased(KeyEvent e) { 
    } 
    public void keyTyped(KeyEvent e) { 
    }    
        
   public void paint(Graphics g) { 
      g.drawImage(backgroundImage, -1000, -1000, this); 
   } 
   
   private BufferedImage loadImg (String pPath){
		URL pic_url = getClass ().getClassLoader ().getResource (pPath);
		BufferedImage source = null;
		try{
			source = ImageIO.read (pic_url);
		}catch (IllegalArgumentException iae){
			iae.printStackTrace();
		}catch (IOException ioe){
			ioe.printStackTrace();
		}
		return source;
	}
}
```

Probier das mal... Habs jetzt nicht nen paar tausendmal auf nen Pfeil gedrückt aber schien mir zu funktioneren! ^^


----------



## Wolfgang Lenhard (28. Jan 2009)

... und wenn man dann noch am Ende der keyPressed-Methode ein g2d.dispose() macht ist es perfekt   :wink:


----------



## manuche (28. Jan 2009)

Nicht zwingend erforderlich da der GarbageCollector das gleich macht allerdings defenitiv die saubere Variante!
Danke werd es direkt noch nachtragen...


----------



## just4fun (28. Jan 2009)

Tausend Dank! Es funktioniert! 

Danke für die schnelle Hilfe! 

Finn


----------

