4

我有一个ImageWrapper类可以将图像保存到磁盘中的临时文件中,以释放堆内存,并允许在需要时重新加载它们。

class ImageWrapper {
    File tempFile;
    public ImageWrapper(BufferedImage img) {
        // save image to tempFile and gc()
    }
    public BufferedImage getImage() {
        // read the image from tempFile and return it.
    }
    public void delete() {
        // delete image from disk.
    }
}

我担心的是,当这样的实例被垃圾收集时,如何确保文件被删除ImageWrapper(否则我可能会用不需要的图像填充磁盘)。这必须在应用程序仍在运行时完成(与终止期间的清理建议相反),因为它可能会运行很长时间。

我对 java 的 GC 概念并不完全熟悉,我想知道这是否finalize()是我正在寻找的。finalize()我的想法是从被覆盖的方法中调用 delete() (就此而言,在单独的线程上) 。这是正确的方法吗?

更新:

我认为我不能close()像许多用户所建议的那样使用对象,因为每个这样的图像都被提取到我无法控制的侦听器列表中,并且可能会保存对该对象的引用。我确定能够删除文件的唯一时间是没有引用,因此我认为finalize()是正确的方法。有什么建议么?

更新 2:

哪些场景finalize()不会被调用?如果唯一的可能是退出程序(以预期/意外的方式),我可以接受它,因为这意味着我只冒着没有删除一个不需要的临时文件的风险(退出期间处理的那个)。

4

6 回答 6

6

另一种方法是使用File.deleteOnExit()标记一个文件,供 JVM 在退出时删除。我意识到这不是要寻找的东西,但可能会引起您的兴趣。

需要明确的是,如果您的 JVM 意外死亡,它不会清除这些文件。因此,您可能希望构建解决方案以在启动时清除缓存文件,这样您就不会随着时间的推移积累大量未使用的缓存文件。

于 2013-08-03T21:17:43.257 回答
3

不推荐使用finalize(). 问题是你不能指望垃圾收集器永远删除一个对象。因此,您放入类的重写finalize()方法中的任何代码都不能保证运行。

于 2013-08-03T21:13:46.483 回答
3

一个很好的替代方案finalizePhantomReference. 最好的使用方法是:

public class FileReference extends PhantomReference<CachedImage> {
  private final File _file;

  public FileReference(CachedImage img, ReferenceQueue<CachedImage> q, File f) {
    super(img, q);
    _file = f;
  }

  public File getFile() {
    _file;
  }
}

然后像这样使用它:

public class CachedImage {

    private static final ReferenceQueue<CachedImage> 
            refQue = new ReferenceQueue<CachedImage>();

    static {
        Thread t = new Thread() {
            @Override
            public void run() {
                try {
                    while (true) {
                        FileReference ref = (FileReference)refQue.remove();
                        File f = ref.getFile();
                        f.delete();
                    }
                } catch (Throwable t) {
                    _log.error(t);
                }
            }
        };
        t.setDaemon(true);
        t.start();
    }

    private final FileReference _ref;

    public CachedImage(BufferedImage bi, File tempFile) {
        tempFile.deleteOnExit();
        saveAndFree(bi, tempFile);
        _ref = new FileReference<CachedImage>(this, refQue, tempFile);
    }
    ...
}
于 2013-08-04T16:59:16.830 回答
2

无法保证您的finalize方法会被调用;特别是,程序退出时挂在周围的任何物体通常都会被扔掉而没有清理。Closeable是一个更好的选择。

于 2013-08-03T21:14:05.700 回答
0

作为@Brian Agnew 答案的替代方案,为什么不安装一个ShutdownHook清除缓存目录的?

public class CleanCacheOnShutdown extends Thread {
  @Override
  public void run() { ... }
}

System.getRuntime().addShutdownHook(new CleanCacheOnShutdown());
于 2013-08-04T14:41:03.437 回答
0

根据这篇文章,我最终使用了File.deleteOnExit()(感谢@Brian)和 a的组合,ScheduledExecutorService它覆盖了ReferenceQueue我的类实例。我添加这个答案是因为没有人建议使用(我认为这是解决我的问题的理想解决方案),我认为这对未来的读者会有帮助。PhantomReferenceReferenceQueue

(有些简化的)结果是这样的(将类名更改为CachedImage):

public class CachedImage {
    private static Map<PhantomReference<CachedImage>, File> 
            refMap = new HashMap<PhantomReference<CachedImage >, File>();
    private static ReferenceQueue<CachedImage> 
            refQue = new ReferenceQueue<CachedImage>();

    static {
        Executors.newScheduledThreadPool(1).scheduleWithFixedDelay(new Thread() {
            @Override
            public void run() {
                try {
                    Reference<? extends CachedImage> phanRef = 
                            refQue.poll();
                    while (phanRef != null) {
                        File f = refMap.get(phanRef);
                        f.delete();
                        phanRef = refQue.poll();

                    }
                } catch (Throwable t) {
                    _log.error(t);
                }
            }
        }, 1, 1, TimeUnit.MINUTES);
    }

    public CachedImage(BufferedImage bi, File tempFile) {
        tempFile.deleteOnExit();
        saveAndFree(bi, tempFile);
        PhantomReference<CachedImage> pref = 
                new PhantomReference<CachedImage>(this, refQue);
        refMap.put(pref, tempFile);
    }
    ...
}
于 2013-08-04T16:47:05.557 回答