# ImageIO.write-Probleme, Speicherüberlauf und Absturz



## -Hades- (28. Feb 2007)

Hi Leute,

ich brauche dringend Hilfe von ein paar Experten die mir mit meinem Halbwissen unter die Arme greifen.

Ich möchte eine Methode schreiben die permanent Screenshots macht (Sie soll ca. 4 Screenshots (45x45 pixel) parallel machen) und die Screenshots dann in Dateien schreibt und diese Dateien dann mit anderen Dateien (die schon vorliegen auf Gleichheit vergleicht. Ich benutze dazu die Robot Klasse und nutze robot.screencapture und ImageIO.write/read um den screenshot zu machen und in eine Datei zu schreiben und dann die Pixel auslesen. 

Soweit so gut, das ganze klappt auch wunderbar solange ich die Methode einmal durchlaufen lasse. Nur wenn ich das jetzt permanent machen lassen möchte gibts Probleme, erst ist das Programm so nach ca. 1 Minute abgestürzt mit der Meldung FileNotFound exception (Zugriff verweigert). der Screenshot wurde plötzlich nicht mehr gemacht oder screateScreencapture ist nicht mehr mitgekommen oder irgendwelche Buffer waren voll, ich weiß es nicht. Mittlerweile läuft es nach einigen Modifikationen ohne diesen Fehler, dafür frisst sich das Programm mit Speicher voll und so nach 15 Minuten bekomme ich die Meldung das der Java Heap Space voll ist, ich habe keine Ahnung wie ich das löse. Ich weiß z.B. nicht ob da immer zusätzlich Speicher verbraucht wird wenn ich File x = new File oder so mache, ich hab versucht so viel wie möglich auszulagern aber klappt alles nicht. Also wenn jemand weiß wie ich das ganze stabil hinbekomme und mir erklärt wie ImageIO usw. genau arbeitet wäre cool. 
Hier bekommt ihr jetzt noch den Code, das ganze überprüft jetzt ob ein Screenshot mit einer aus 6 vorgegebenen Dateien übereinstimmt, und das ganze muss ich ja mindestens 4 mal haben. Es darf also nicht abstürzen, muss dennoch recht zügig arbeiten und der Speicher sollte sich nicht so zufressen.

Ihr seht im Code sowas wie xx.flush(); und sowas, ich weiß allerdings nicht ob das überhaupt irgendwas bringt, habs irgendwo aufgeschnappt, also bitte ich um Experten die mich aufklären können.

Auch Sachen wie while (!tempshot.canread()) {this.sleep (10);} da weiß ich nicht obs irgendeinen sinn hat...

Danke im voraus,  Gruß Dominik




```
public class Scanne extends Thread{
    public static int Zaehler = 1;
    public String[] FileNamesL1 = {"w1_1","w1_2","w1_3","w1_4","w1_5","w1_6"};
    public static int Welcher = 0;
    public boolean musswarten = false;
                Robot superhirn = new Robot();
                File tempshot = new File("tempshot.gif");
             Rectangle maßeL1 = new Rectangle(465,416,43,46);
     /** Creates a new instance of Scanne*/
    public Scanne() throws IOException, AWTException {
            ImageIO.write(superhirn.createScreenCapture(maßeL1), "gif", tempshot);
    }

    public void ScanningL1() throws IOException, InterruptedException {
                     ImageIO.setUseCache(false);
                     File extern = new File ("extern.gif");
            File datei = new File( FileNamesL1[Welcher] );
            while (!datei.canRead()) {this.sleep(5);}
            Image image2 = ImageIO.read(datei);
            
            BufferedImage imagex = (BufferedImage) image2;
            if (tempshot.canWrite() && tempshot.canExecute()) {if (Welcher == 0) {ImageIO.write(superhirn.createScreenCapture(maßeL1), "gif", tempshot);}}
            if (!tempshot.canWrite() || !tempshot.canExecute()) {
                tempshot.delete();
                this.sleep(10);
                tempshot.renameTo(extern);
                File tempshot = new File("tempshot.gif");
                ImageIO.write(superhirn.createScreenCapture(maßeL1), "gif", tempshot);
            }
            
            
            while (!tempshot.canRead()) {this.sleep(10);}
            Image tempimage = ImageIO.read(tempshot);
            BufferedImage tempimagex = (BufferedImage) tempimage;
          tempimage.flush();
          tempimagex.flush();

            int pixelarray[][] = new int[tempimagex.getWidth()][tempimagex.getHeight()];
            int pixelarray2[][] = new int[imagex.getWidth()][imagex.getHeight()];            
            boolean gleich = true;
           // if (imagex.hashCode() != tempimagex.hashCode()) {gleich = false;}
            for (int i =0;i < tempimagex.getWidth();i++) {
                for (int j = 0;j < tempimagex.getHeight();j++) {
                    pixelarray[i][j] = tempimagex.getRGB(i,j);
                    pixelarray2[i][j] = imagex.getRGB(i,j);
                    System.out.println("Farbwert an Position "+i+", "+j+" = "+pixelarray[i][j]);
                    if (pixelarray[i][j] != pixelarray2[i][j]) {gleich = false;break;}
                 }if (!gleich) {break;}
            }
             
             if (gleich && (musswarten==false)) {
                System.out.println("Erkannt");WriteTXTL1(Welcher);musswarten = true;}
             if (!gleich) {Welcher = (WelcherWuerfel + 1) %6;}
             if (!gleich && (Welcher == 5)) {musswarten = false;}
          tempimage.flush();
          tempimagex.flush();
          tempshot.delete();
          this.sleep(100);
          this.ScanningL1();
        
       
  }
```


----------



## HaBaLeS (1. Mrz 2007)

Erstmal solltest du deinen Code sauber formatieren, das hilfr ungemein bei der Fehlersuche  :wink: 

ganz untern rufst du "this.ScanningL1(); "  auf. Ich habe jedoch kein Abbruchkriterium gefunden.  Kann es sein, das du dich in einer Endlosrekursion befindest, die dir den Speicher wegfrisst?

Generell solltest du beachten: 

alle Onjekte die du nicht mehr brauchst null setzen, damit sie aufgeräumt werden können.
rekursionen ohne abbruchkriterium führen garantiert irgendwan zu Out Of Memory
wenn du die funktion endlos laufen lassen willst, trigger sie xtern in ner schleife


----------



## Gast (1. Mrz 2007)

hrhr, wo soll denn da eine Rekursion sein ?
Es Wird ja kein Abbruch benoetigt, wofuer auch, das Dingen soll ja in einer "Endlosschleife" laufen"
Von einer anderen methode das dingen zu "triggern" wuerde ja nochmehr auslastung bedeuten.


----------



## HaBaLeS (1. Mrz 2007)

Soso.....


```
public void ScanningL1() throws IOException, InterruptedException {
           
         .... viel schlecht lesbarer code ....

          this.ScanningL1();
  }
```

wenn das keine Rekursion ist! Wie bezeichnest du es wenn eine Funktion sich selbst aufruft? Selbst wenn die Funktion keinen neuen Speicher allokiert, wird der Code wegen des wachsenden Call-Stacks früher oder später Out-Of-Memory laufen.


----------



## -Hades- (1. Mrz 2007)

Hi,

danke schonmal für die Antworten und irgendwie kann ich euch allen zustimmen, einerseits hast du recht Habales, ich rufe die Methode am Ende (bewusst) wieder auf und es sieht wie ne Rekursion aus aber Gast muss ich auch recht geben, es ist ja nicht eine Rekursion in dem Sinne (oder?) ich meine die Methode is ja komplett abgearbeitet und erst dann (am Ende) rufe ich sie erneut auf, da ich das dingen ja endlos laufen lassen möchte. 
Kannst du mir kurz beschreiben wie das gemeint ist mit dem externen triggern der schleife?

Mit der Formatierung gebe ich euch auch recht, das wird auch geändert.

Aber mal abgesehen von dem Speicherproblem, weiß denn jemand was darüber das er mir nach ner Zeit die Meldung: FileNotFound exception (Zugriff verweigert) tempshot (nicht ganz originalgetreu) ausspuckt? Also der Screenshot wird so wie ichs beobachtet habe plötzlich nicht mehr angelegt (irgendwelche Buffer von ImageIO voll oder was auch immer). Ich hoffe ihr habt dafür Antworten.

Das mit dem auf null setzen der Variablen werd ich sofort korrigieren!

Nochmal danke für die Antworten und ich hoffe ich könnt mir nochmals helfen.

Gruß Dominik


----------



## HaBaLeS (1. Mrz 2007)

Erstmal zu dem "FileNotFound exception (Zugriff verweigert)"

Bei dem meisten Image/Grafik Operationen ist es so, das der return sofort kommt. d.h. es wird nicht gewartet bis die Operation komplett abgeschlossen ist sondern die Verarbeitung direkt weiterläuft und die angforderte Operation im Hintergrund läuft. (mehr dazu in den entsprechenden API-Docs). Dies hängt damit zusammen, das diese Funktionen meist nativ sind. Nachdem du keine besonders langwirigen Berechnungen machst, sondern die Methode direkt wieder aufrufst kann es sein, das du "Race Conditions" bekommst. z.B: löschen des Files wärend noch reingeschrieben wird oder 2 Bilder gleichzeitig in die Datei schreiben etc... (sind nur beispiele keine konkreten vermutungen) . 

Um dies zu verhindern hast du ja selbst schon sleeps eingebaut. eine weiter Möglichkeit wäre in verschiedene temporäre Dateien zu schreiben. (aufräumennicht vergessen.)



Nun zur Rekursion:
wie weit du die Methode abgearbeitet hast spielt überhaupt keine  Rolle. Sobald eine funtion sich selbst aufruft ist es eine Rekursion. Ohne abbruchkriterium hast du IMMER ein Problem.

Warum machst du nciht an der stelle an der du deine funtion aufrufst ein :


```
private boolean running = true;
while(running){
 running = ScanningL1();
}
```

Damit wird die Funktion in einer Endlosschleife aufgerufen solage sie true returnt. Die Schleife kannst du beenden, indem du aus der funktion ein false returnst.


----------



## -Hades- (1. Mrz 2007)

Danke für die schnellen Antworten Habales!!

Ich werde deine Vorschläge direkt umsetzen. 
Das mit den race conditions hab ich mir fast schon gedacht aber ich hab in den docs nichts dahingehendes gefunden aber es stimmt das die Methoden die versuchen die Datei zu schreiben ect. nativ sind.
Das mit der Schleife werd ich auch so machen, ich werde Bescheid geben inwieweit das ganze sich verbessert/stabilisiert hat danach!

Gruß Dominik


----------



## -Hades- (1. Mrz 2007)

So, ich hab den Code ein wenig umgeschrieben und das Speicherproblem is annähernd gelöst, jedenfalls wächst er weitaus langsamer an, was aber hoffentlich noch ganz gelöst werden kann.

Das andere Problem besteht aber weiterhin, ich bekomme die FileNotFound exception nicht mehr und wenn er kein Bild findet das exakt gleich ist läuft auch alles prima bloß wenn er eins findet, dann macht er keine neuen Screenshots mehr wie mir scheint, er bleibt dann beim letzten hängen und vergleicht immer nur das.
Vielleicht geht bei der Modulo-Rechnung was schief oder so aber ich sehe nix, entweder ich bin ziemlich verpeilt gerade oder der Fehler is doch schwerer zu entdecken, vielleicht findet ihr ja was.

Ich lasse jetzt erst einen Screenshot machen und vergleiche dann erst diesen shot mit alle 6 vorliegenden Dateien und dann wird erst ein neuer shot gemacht. Ihr werdet es ja sehen:


```
public class Scanne extends Thread{
    //public static int Durchlauf = 0;
    public static int Zaehler = 1;
    public static int DateiZaehler = 0;
    public String[] TempDateiName = {"tempshot1.gif","tempshot2.gif","tempshot3.gif","tempshot4.gif","tempshot5.gif","tempshot6.gif"};
    public String[] FileNamesL1 = {"wuerfell1_1","w1_2","w1_3","w1_4","w1_5","w1_6"};
    public static int Welcher = 0;
    public boolean musswarten = false;
    int pixelarray[][];
    int pixelarray2[][];
    Robot superhirn = new Robot();
    Rectangle maßeL1 = new Rectangle(465,416,43,46);
    public Scanne() throws IOException, AWTException {
    }
    public boolean ScanningL1() throws IOException, InterruptedException {
        File tempshot = new File(TempDateiName[DateiZaehler]);
        ImageIO.setUseCache(false);
        //if (tempshot.canWrite() && tempshot.canExecute()) {
            ImageIO.write(superhirn.createScreenCapture(maßeL1), "gif", tempshot);
            DateiZaehler = (DateiZaehler + 1) % 6;
        //}
        Image tempimage = ImageIO.read(tempshot);
        BufferedImage tempimagex = (BufferedImage) tempimage;
        while (Welcher < 6) {
            File datei = new File( FileNamesL1[Welcher] );
            while (!datei.canRead()) {this.sleep(5);}
            Image image2 = ImageIO.read(datei);
            BufferedImage imagex = (BufferedImage) image2;
            
            int pixelarray[][] = new int[tempimagex.getWidth()][tempimagex.getHeight()];
            int pixelarray2[][] = new int[imagex.getWidth()][imagex.getHeight()];
            boolean gleich = true;
            // if (imagex.hashCode() != tempimagex.hashCode()) {gleich = false;}
            for (int i =0;i < tempimagex.getWidth();i++) {
                for (int j = 0;j < tempimagex.getHeight();j++) {
                    pixelarray[i][j] = tempimagex.getRGB(i,j);
                    pixelarray2[i][j] = imagex.getRGB(i,j);
                    System.out.println("Farbwert an Position "+i+", "+j+" = "+pixelarray[i][j]);
                    if (pixelarray[i][j] != pixelarray2[i][j]) {
                        gleich = false;break;
                    }
                }
                if (!gleich) {
                    break;
                }
            }
            
            if (gleich && (musswarten==false)) {
                System.out.println("Erkannt!");
                WriteTXTL1(Welcher);
                Welcher = 6;
                //           tempshot = null;
                datei = null;
                musswarten = true;
                break;
            }
            if (!gleich) {
                System.out.println(Welcher);
                Welcher = (Welcher + 1) ;
            }
            if (!gleich && (Welcher == 6)) {
                musswarten = false;
                //           tempshot = null;
            }
        }
        tempimage = null;
        tempimagex = null;
        tempshot = null;
        pixelarray = null;
        pixelarray2 = null;
        Welcher = 0;
        this.sleep(100);
        return true;
        
    }
```


----------



## HaBaLeS (2. Mrz 2007)

Eine Anmerkung vorweg. Schreib bitte Variablem mit kleinem Buchstaben am Anfang.  Das ist gängige Codeconvention.


Du hast hier eine Klassenvariable:


```
public static int Welcher = 0;
```

Warum ist die public?  Warum ist die static ? Greifst du van anderen Klassen darauf zu? 
Vom context des Codes den ich hier sehe, wäre private angebracht.


Du gehtst nur in die Schleife, wenn  weicher<6

```
while (Welcher < 6) {
           ... vergleich der bilder ....
      }
```


Falls das du einen Treffer hattest setzt du die *Klassenvariable*  weicher= 6

```
if (gleich && (musswarten==false)) {
               .....
                Welcher = 6;
               .....
            }
```



Problem erkannt? Er Vergleicht nicht das den alten Screenshot sondern garncihtmehr.
Kleiner Tipp, wann wird weicher wieder 0?


P.S. Verrätst du mir, was das Programm macht das du schreibst.


----------



## -Hades- (2. Mrz 2007)

Hi Dank dir, das public static is Quatsch, das is auch nur weil da nicht so drauf geachtet habe, die Variable wird nicht woanders benutzt.

Den Fehler habe ich erkannt, das Problem war nicht das Welcher nicht auf 0 geht (das passiert ganz am Ende der Methode) sondern das da eine Endlosschleife enstanden ist wenn er ein Bild gefunden hat, er aber noch warten muss (boolean musswarten auf true), das hab ich geändert indem ich am Ende der Schleife: Welcher +=1; geschrieben hab, jetzt läuft es so wie ichs möchte.

SO, jetzt bleibt eigentlich "nur" noch der Fehler mit dem FileNotFound Zugriff verweigert und ich kann mir das immer noch nicht erklären.

Ich habe ein bisschen im Netz gesucht und bin auf mehrere Seiten gestoßen auf denen dasselbe Problem erläutert wird. Es scheint so als wenn ImageIO.write bugbehaftet ist und manchmal leere Dateien schreibt, warum auch immer.

Einige Beispiele:

ImageIO.write bug

ImageIO.write bug


Wenn das jemand bestätigen kann oder sogar wie mans löst wäre ich sehr dankbar!

Gruß Dominik


----------



## HaBaLeS (2. Mrz 2007)

Das letzte Weicher= 0 hab ich wohl übersehen.  :shock: 

Was du gegen den Bug machen kannst weiss ich leider nicht. Du kannst jedoch falls es die Rahmenbedingungen deines Programms erlauben ein sauberes Fehlerhandling für dieses Problem schreiben, sodass dein Programm troz JVM Bug weiterläuft.  

Damit meine ich z.B. die Exception an der stelle fangen an der sie fliegt (was übrigens immer sinvoll ist!) und dann darauf reagieren, indem du die methode neu startest. In so einem Fall wäre eine Rekursion sogar nciht verkehrt.

Viel Erfolg  
 HaBaLeS


----------



## -Hades- (2. Mrz 2007)

Das ist ne gute Idee, das fehlerhafte ImageIO abfangen und erneut schreiben lassen. Habales ich dank dir für deine ganze Hile und Mühe!! Cool solche Leute wie dich hier zu haben.

Wäre übrigens cool wenn wir uns vielleicht ma über n messenger oder so unterhalten könnten, ich kann noch einiges von dir lernen.

Also falls du INteresse hast schreib mir doch bitte eine Mail an: dominik.thalmann@uni-dortmund.de

Ich hoffe weiterhin das vielleicht jemand eine Lösung für das Problem/ eventuell bug hat.

Gruß Dominik


----------



## Marco13 (2. Mrz 2007)

Prinzipiell kannst du auch so ein Image in einen (File)OutputStream rausschreiben


```
import com.sun.image.codec.jpeg.*;
...

        JPEGImageEncoder encoder = JPEGCodec.createJPEGEncoder(outputStream);
        JPEGEncodeParam param = encoder.getDefaultJPEGEncodeParam(image);
        param.setQuality(quality, false);
        encoder.setJPEGEncodeParam(param);
        encoder.encode(image);
```
Aber das dabei verwendete Package ist "eigentlich" nicht für den öffentlichen Gebrauch gedacht (ich glaube, bei Java 1.6 kommt schon eine Meldung, dass das in zukünftigen Versionen so evtl. nichtmehr funtionieren wird)


----------

