# File.deleteOnExit() funktioniert nicht



## 0001001 (20. Dez 2010)

Hallo,

ich lege für eine Operation ein Temp-File an, das ich nach Beenden des Programms gerne wieder löschen würde. Hierzu habe ich die Methode File.deleteOnExit() gefunden. Diese funktioniert allerdings nicht, das Temp-File wird nicht gelöscht.

Was mache ich falsch?

Hier eine Beispielanwendung die das Problem demonstriert

```
import java.awt.Graphics2D;
import java.awt.Point;
import java.awt.image.BufferedImage;
import java.awt.image.ColorModel;
import java.awt.image.DataBuffer;
import java.awt.image.DirectColorModel;
import java.awt.image.SampleModel;
import java.awt.image.WritableRaster;
import java.io.File;
import java.io.IOException;
import java.io.RandomAccessFile;
import java.nio.ByteBuffer;
import java.nio.IntBuffer;
import java.nio.channels.FileChannel;
import javax.imageio.ImageIO;
import javax.imageio.stream.FileImageOutputStream;
import javax.swing.JPanel;
import org.openide.util.Exceptions;

public class Test {
    public static void main(String[] argv) {
        Test test = new Test();
        test.createAndDeleteTempFile();
    }

    public void createAndDeleteTempFile() {
        try {
            JPanel panel = new JPanel();
            panel.setSize(800, 600);
            int w = panel.getBounds().width;
            int h = panel.getBounds().height;

            File file2 = File.createTempFile("tempfile", "tmp");
            file2.deleteOnExit();   // THIS DOES NOT WORK

            FileChannel rwChannel = new RandomAccessFile(file2, "rw").getChannel();
            ByteBuffer wrBuf = rwChannel.map(FileChannel.MapMode.READ_WRITE, 0, 6 * w * h);
            IntBuffer iBuf = wrBuf.asIntBuffer();

            DataBufferSubClass db = new DataBufferSubClass(DataBuffer.TYPE_INT, 3 * h * w);
            db.setWrBuf(iBuf);

            ColorModel cm = new DirectColorModel(24, 0x00ff0000, 0x0000ff00, 0x000000ff);
            SampleModel sm = cm.createCompatibleSampleModel(w, h);
            WritableRaster wr = new MyWritableRaster(sm, db, new Point(0, 0));
            BufferedImage bim = new BufferedImage(cm, wr, true, null);

            Graphics2D g = bim.createGraphics();
            panel.paint(g);

            FileImageOutputStream fo = new FileImageOutputStream(
                    new File(System.getProperty("user.dir")+
                    System.getProperty("file.separator")+"out.png"));
            ImageIO.write(bim, "" + "PNG", fo);
        } catch (IOException ex) {
            Exceptions.printStackTrace(ex);
        }
    }

    public class DataBufferSubClass extends DataBuffer {
        private IntBuffer wrBuf;

        public void setWrBuf(IntBuffer wrBuf) {
            this.wrBuf = wrBuf;
        }

        public DataBufferSubClass(int dataType, int size) {
            super(dataType, size);
        }

        public DataBufferSubClass(int dataType, int size, int numBanks) {
            super(dataType, size, numBanks);
        }

        public DataBufferSubClass(int dataType, int size, int numBanks, int offset) {
            super(dataType, size, numBanks, offset);
        }

        public DataBufferSubClass(int dataType, int size, int numBanks, int[] offsets) {
            super(dataType, size, numBanks, offsets);
        }

        @Override
        public int getElem(int bank, int i) {
            return wrBuf.get(i);
        }

        @Override
        public void setElem(int bank, int i, int val) {
            wrBuf.put(i, val);
        }
    }

    public class MyWritableRaster extends WritableRaster {
        public MyWritableRaster(SampleModel sampleModel, DataBuffer dataBuffer, Point origin) {
            super(sampleModel, dataBuffer, origin);
        }
    }
}
```


----------



## tfa (20. Dez 2010)

Wie wär's mit File schließen? Vielleicht hilft das ja. Aber auch sonst, ist das immer eine gute Idee.


----------



## 0001001 (20. Dez 2010)

Das hilft leider nicht.


----------



## Ark (20. Dez 2010)

Zu [c]deleteOnExit()[/c] heißt es: "Deletion will be attempted only for normal termination of the virtual machine, as defined by the Java Language Specification." Bist du dir in diesem Punkt sicher?

Ark


----------



## 0001001 (20. Dez 2010)

Ja,
die JVM wird normal beendet.

Unter Linux funktioniert der Code und die Datei wird gelöscht. Scheint also ein Problem mit der JVM für Windows bzw. Windows 7 sein. Bin völlig ratlos :-(


----------



## bygones (20. Dez 2010)

berechtigungen korrekt ?! also darf der user tmp files löschen ?

ka wie das unter Win bzw vor allem win 7 so ist... daher ins blaue geraten


----------



## 0001001 (20. Dez 2010)

Ja, 
manuelles löschen funktioniert. file.iswriteable() liefert auch true zurück.

Wenn ich allerdings folgendes mache, bekomme ich eine AccessDeniedException:
SecurityManager sm = new SecurityManager();
sm.checkDelete(file.getAbsolutePath());


----------



## HoaX (20. Dez 2010)

Wenn du dir sicher bist dass die Berechtigungen stimmen und es unter Linux funktioniert, dann liegt is zu 99% am nicht richtig schließen der Datei vor dem Beenden. Zeig doch mal wie der Code nun mit dem Schließen aussieht.


----------



## 0001001 (21. Dez 2010)

Hier der Code mit Schließen:

```
import java.awt.Graphics2D;
import java.awt.Point;
import java.awt.image.BufferedImage;
import java.awt.image.ColorModel;
import java.awt.image.DataBuffer;
import java.awt.image.DirectColorModel;
import java.awt.image.SampleModel;
import java.awt.image.WritableRaster;
import java.io.File;
import java.io.IOException;
import java.io.RandomAccessFile;
import java.nio.ByteBuffer;
import java.nio.IntBuffer;
import java.nio.channels.FileChannel;
import javax.imageio.ImageIO;
import javax.imageio.stream.FileImageOutputStream;
import javax.swing.JPanel;
import org.openide.util.Exceptions;

public class Test {

    public static void main(String[] argv) {
        Test test = new Test();
        test.createAndDeleteTempFile();
    }

    public void createAndDeleteTempFile() {
        try {
            JPanel panel = new JPanel();
            panel.setSize(800, 600);
            int w = panel.getBounds().width;
            int h = panel.getBounds().height;

            //  File file2 = File.createTempFile("tempfile", ".tmp");
            File file2 = new File("test.tmp");
            file2.deleteOnExit();   // THIS DOES NOT WORK

            FileChannel rwChannel = new RandomAccessFile(file2, "rw").getChannel();
            ByteBuffer wrBuf = rwChannel.map(FileChannel.MapMode.READ_WRITE, 0, 6 * w * h);
            IntBuffer iBuf = wrBuf.asIntBuffer();

            DataBufferSubClass db = new DataBufferSubClass(DataBuffer.TYPE_INT, 3 * h * w);
            db.setWrBuf(iBuf);

            ColorModel cm = new DirectColorModel(24, 0x00ff0000, 0x0000ff00, 0x000000ff);
            SampleModel sm = cm.createCompatibleSampleModel(w, h);
            WritableRaster wr = new MyWritableRaster(sm, db, new Point(0, 0));
            BufferedImage bim = new BufferedImage(cm, wr, true, null);

            Graphics2D g = bim.createGraphics();
            panel.paint(g);
            g.dispose();
            System.gc();
            
            FileImageOutputStream fo = new FileImageOutputStream(
                    new File(System.getProperty("user.dir")
                    + System.getProperty("file.separator") + "out.png"));

            ImageIO.write(bim, "" + "PNG", fo);

            fo.close();
            bim = null;
            db = null;
            fo = null;
            bim = null;
            iBuf = null;
            wrBuf = null;
            rwChannel.close();
            rwChannel = null;

            System.gc();
            file2.delete();

        } catch (IOException ex) {
            Exceptions.printStackTrace(ex);
        }
    }

    public class DataBufferSubClass extends DataBuffer {

        private IntBuffer wrBuf;

        public void setWrBuf(IntBuffer wrBuf) {
            this.wrBuf = wrBuf;
        }

        public DataBufferSubClass(int dataType, int size) {
            super(dataType, size);
        }

        public DataBufferSubClass(int dataType, int size, int numBanks) {
            super(dataType, size, numBanks);
        }

        public DataBufferSubClass(int dataType, int size, int numBanks, int offset) {
            super(dataType, size, numBanks, offset);
        }

        public DataBufferSubClass(int dataType, int size, int numBanks, int[] offsets) {
            super(dataType, size, numBanks, offsets);
        }

        @Override
        public int getElem(int bank, int i) {
            return wrBuf.get(i);
        }

        @Override
        public void setElem(int bank, int i, int val) {
            wrBuf.put(i, val);
        }
    }

    public class MyWritableRaster extends WritableRaster {

        public MyWritableRaster(SampleModel sampleModel, DataBuffer dataBuffer, Point origin) {
            super(sampleModel, dataBuffer, origin);
        }
    }
}
```


----------



## bygones (21. Dez 2010)

wieso rufst du noch explizit [c]file2.delete();[/c] auf, wenn du vorher [c]file2.deleteOnExit();[/c] schreibst ?

weiterhin: close() immer in einen finally block rein... bei einer Exception wird dein stream nicht geschlossen....


----------



## 0001001 (21. Dez 2010)

Weil ich sichergehen will dass die Datei gelöscht wird, spielt aber keine Rolle, sie wird nicht gelöscht, weder mit noch ohne file2.delete();

Hat jemand den Code mal bei sich unter Windows 7 getestet?


----------



## tuxedo (21. Dez 2010)

Also dein Beispielcode löscht bei mir unter Win7 64bit und Java 1.6 Update 20 die File auch nicht.

Allerdings klappt es hier mit dem löschen bestens:


```
import java.io.File;
import java.io.FileWriter;
import java.io.IOException;
 
public class Test {
 
    public static void main(String[] argv) throws IOException {
        File f = new File("test.tmp");
        f.deleteOnExit();
        
        FileWriter fw = new FileWriter(f);
        fw.write("Hallo Welt");
        fw.close();
    }
 
}
```

Hab mir aber noch nicht die Mühe gemacht zu schauen wo jetzt der Unterschied zwischen deinem und meinem Vorgehen ist.

- Alex


----------



## 0001001 (21. Dez 2010)

Danke schonmal für den Hinweis.

Ich vermute, dass es daran liegt, dass ich die Datei als Cache für BufferedImage verwende und diese dann *aus welchem Grund auch immer* noch nicht freigegeben ist wenn ich sie löschen will. Warum das so ist, verstehe ich nicht :-/

Bin weiterhin über jede Hilfe dankbar!


----------



## HoaX (21. Dez 2010)

Das Problem ist, dass du das RandomAccessFile nicht schließt, sondern nur den damit verbundenen Channel.


----------



## tuxedo (21. Dez 2010)

hab ich ausprobiert. Bringt auch nix. 


```
import java.awt.Graphics2D;
import java.awt.Point;
import java.awt.image.BufferedImage;
import java.awt.image.ColorModel;
import java.awt.image.DataBuffer;
import java.awt.image.DirectColorModel;
import java.awt.image.SampleModel;
import java.awt.image.WritableRaster;
import java.io.File;
import java.io.IOException;
import java.io.RandomAccessFile;
import java.nio.ByteBuffer;
import java.nio.IntBuffer;
import java.nio.channels.FileChannel;
import javax.imageio.ImageIO;
import javax.imageio.stream.FileImageOutputStream;
import javax.swing.JPanel;
 
public class Test {
 
    public static void main(String[] argv) {
        Test test = new Test();
        test.createAndDeleteTempFile();
    }
 
    public void createAndDeleteTempFile() {
        try {
            JPanel panel = new JPanel();
            panel.setSize(800, 600);
            int w = panel.getBounds().width;
            int h = panel.getBounds().height;
 
            //  File file2 = File.createTempFile("tempfile", ".tmp");
            File file2 = new File("test.tmp");
            file2.deleteOnExit();   // THIS DOES NOT WORK
            RandomAccessFile randomAccessFile = new RandomAccessFile(file2, "rw");
            FileChannel rwChannel = randomAccessFile.getChannel();
            ByteBuffer wrBuf = rwChannel.map(FileChannel.MapMode.READ_WRITE, 0, 6 * w * h);
            IntBuffer iBuf = wrBuf.asIntBuffer();
 
            DataBufferSubClass db = new DataBufferSubClass(DataBuffer.TYPE_INT, 3 * h * w);
            db.setWrBuf(iBuf);
 
            ColorModel cm = new DirectColorModel(24, 0x00ff0000, 0x0000ff00, 0x000000ff);
            SampleModel sm = cm.createCompatibleSampleModel(w, h);
            WritableRaster wr = new MyWritableRaster(sm, db, new Point(0, 0));
            BufferedImage bim = new BufferedImage(cm, wr, true, null);
 
            Graphics2D g = bim.createGraphics();
            panel.paint(g);
            g.dispose();
            
            FileImageOutputStream fo = new FileImageOutputStream(
                    new File(System.getProperty("user.dir")
                    + System.getProperty("file.separator") + "out.png"));
 
            ImageIO.write(bim, "" + "PNG", fo);
            bim.flush();
            rwChannel.close();
            randomAccessFile.close();
            fo.close();
            bim=null;
 
 
        } catch (IOException ex) {
            ex.printStackTrace();
        }
    }
 
    public class DataBufferSubClass extends DataBuffer {
 
        private IntBuffer wrBuf;
 
        public void setWrBuf(IntBuffer wrBuf) {
            this.wrBuf = wrBuf;
        }
 
        public DataBufferSubClass(int dataType, int size) {
            super(dataType, size);
        }
 
        public DataBufferSubClass(int dataType, int size, int numBanks) {
            super(dataType, size, numBanks);
        }
 
        public DataBufferSubClass(int dataType, int size, int numBanks, int offset) {
            super(dataType, size, numBanks, offset);
        }
 
        public DataBufferSubClass(int dataType, int size, int numBanks, int[] offsets) {
            super(dataType, size, numBanks, offsets);
        }
 
        @Override
        public int getElem(int bank, int i) {
            return wrBuf.get(i);
        }
 
        @Override
        public void setElem(int bank, int i, int val) {
            wrBuf.put(i, val);
        }
    }
 
    public class MyWritableRaster extends WritableRaster {
 
        public MyWritableRaster(SampleModel sampleModel, DataBuffer dataBuffer, Point origin) {
            super(sampleModel, dataBuffer, origin);
        }
    }
}
```

- Alex


----------



## HoaX (21. Dez 2010)

Ich habe kein Windows, kanns nicht wirklich testen. Aber es war vielversprechend ...
Dann ist meine nächste Vermutung dass es am FileChannel#map liegt, die Javadoc dazu spricht ja Bände.
ich frage mich sowieso, für was du da das File brauchst. Ein Bild mit 800x600 sollte ohne Probleme auch so im Speicher zu halten sein?!


----------



## 0001001 (21. Dez 2010)

Das ist nur ein Beispiel. Mein wirkliches Bild ist ca. 12000 x 10000.


----------



## HoaX (21. Dez 2010)

Hm, dann komm ich grob 400-500Mb fürs Bild. Hast du keine Möglichkeit der JVM mehr Speicher zu geben? Zur Not lagert ja dann das OS auf die Festplatte aus, du kannst also eigentlich nichts verlieren. Wenn du immer direkt selbst auslagerst, dann wird das doch relativ langsam, oder nicht?


----------



## 0001001 (21. Dez 2010)

Ich hab lange getüftelt bis ich diese Version mit dem Cache-basierten BufferedImage hinbekommen habe. Leider kann ich nicht vorhersehen wie groß das jeweilige Panel wird daher brauche ich diese Variante. 

Es muss doch möglich sein alle Referenzen auf das File zu enfernen und dann das File zu löschen.


----------



## HoaX (21. Dez 2010)

0001001 hat gesagt.:


> Es muss doch möglich sein alle Referenzen auf das File zu enfernen und dann das File zu löschen.



Wenn ich die Javadoc zu FileChannel#map richtig verstehe, dann nicht.


----------



## tuxedo (21. Dez 2010)

Was wäre dennso tragisch daran die File einfach im Cache/Temp Verzeichnis liegen zu lassen? Beim nächsten Programmstart wird die doch neu verwendet und somit überschrieben, oder?

Würde heissen dass halt ein paar hundert MB unnötigerweise auf der Platte rumliegen. Sollte aber bei einigermaßen aktuellen Systemen ja nicht so das Problem sein, oder?

- Alex

[update]
Musst halt die File nicht beim beenden löschen, sondern beim Programmstart. Dann ist sie immer nur so groß, wie sie beim letzten mal ausführen angelegt wurde.


----------



## Sonecc (21. Dez 2010)

Und da zeigt sich (mal wieder) ein Problem heutiger Entwickler... Der maßlose umgang mit Resourcen


----------



## tuxedo (21. Dez 2010)

Naja, muss jeder selbst wissen wieviel Aufwand er für so einen "kleinen" Nebeneffekt spendiert. 

Alternativ könnte man um die eigentliche Anwendung noch ne Anwendung drum rum spannen die nach dem beenden der eigentlichen Anwendung aufräumt.


----------



## tfa (21. Dez 2010)

> Unter Linux funktioniert der Code und die Datei wird gelöscht. Scheint also ein Problem mit der JVM  für Windows bzw. Windows 7 sein. Bin völlig ratlos


Das liegt wohl eher an Windows als an der JVM. Sobald eine Datei noch irgendwo verwendet wird - z.B. irgendwo im Netzwerk ist in einem Explorer das Verzeichnis geöffnet - kann Windows die nicht löschen. Der totale Irrsinn.

Als Workaround könntest du einfach beim Programmneustart dein Temp-Verzeichnis durchgehen und alle nicht mehr gebrauchten Dateien löschen. Das sollte wenigstens funktionieren.


----------



## HoaX (21. Dez 2010)

Das mit dem Datei einfach beim Starten löschen ist doch eine gute Idee, finde ich. Vorallem falls es funktioniert, beim Beenden des Programms mit RandomAccessFile#setLength die Datei noch auf 0 Bytes zu verkleinern.


----------



## 0001001 (21. Dez 2010)

Hallo,
vielen Dank für eure Hilfe! Habs nun gelöst.

Was man hätte machen können wäre eine Liste der Dateien zu führen und periodisch in nem eigenen Thread versuchen diese Dateien zu löschen (irgendwann muss der Garbage Collector den auf null gesetzten Buffer ja einsammeln und damit das Mapping aufheben).

Ich habe folgendes gemacht: Nach dem Verwenden der Datei, schreibe ich ein byte in die Datei, das veringert schon mal die Größe. Danach versuche ich die Datei zu löschen. Zudem verwende ich immer die gleiche Datei im Temp Ordner, so dass dort maximal 1 Datei mit der Größe 1kb liegt.


----------

