Java 在我的例子中使用finalize()?

Java 在我的例子中使用finalize()?,java,garbage-collection,finalizer,Java,Garbage Collection,Finalizer,我有一个ImageWrapper类,它将图像保存到磁盘中的临时文件中,以释放堆内存,并允许在需要时重新加载它们 class ImageWrapper { File tempFile; public ImageWrapper(BufferedImage img) { // save image to tempFile and gc() } public BufferedImage getImage() { // read the im

我有一个
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()
?如果唯一可能的是退出程序(以预期/意外的方式),我可以接受,因为这意味着我只有一个不需要的临时文件未被删除(退出时处理的文件)。

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

不能保证调用
finalize
方法;特别是,当程序退出时,任何挂起的对象通常都会被扔掉,而无需清理<代码>可关闭是一个更好的选择。

另一种方法是使用它标记JVM退出时要删除的文件。我意识到这不是你想要的,但可能是你感兴趣的


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

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

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

System.getRuntime().addShutdownHook(new CleanCacheOnShutdown());

我最终使用了
File.deleteOnExit()
(谢谢@Brian)和
ScheduledExecutorService
的组合,后者通过
PhantomReference
ReferenceQueue
进入我的类实例。 我之所以添加这个答案,是因为没有人建议使用
ReferenceQueue
(我认为这是解决我的问题的理想方法),我认为这将对未来的读者有所帮助

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

公共类CachedImage{
私有静态映射
refMap=newhashmap();
私有静态引用队列
refQue=新引用队列();
静止的{
Executors.newScheduledThreadPool(1).scheduleWithFixedDelay(新线程(){
@凌驾
公开募捐{
试一试{

参考除了
finalize
之外,还有一个很好的替代方法是
PhantomReference
。使用它的最佳方法是:

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;
  }
}
公共类文件引用扩展了PhantomReference{
私有最终文件_文件;
公共文件引用(CachedImage img、ReferenceQueue q、文件f){
超级(img,q);
_file=f;
}
公共文件getFile(){
_档案;
}
}
然后像这样使用它:

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);
    }
    ...
}
公共类CachedImage{
私有静态最终引用队列
refQue=新引用队列();
静止的{
线程t=新线程(){
@凌驾
公开募捐{
试一试{
while(true){
FileReference ref=(FileReference)refQue.remove();
文件f=ref.getFile();
f、 删除();
}
}捕获(可丢弃的t){
_对数误差(t);
}
}
};
t、 setDaemon(true);
t、 start();
}
私人最终文件参考_ref;
公共缓存图像(BuffereImage bi,文件tempFile){
tempFile.deleteOnExit();
saveAndFree(bi,tempFile);
_ref=新文件引用(this、refQue、tempFile);
}
...
}

这确实很有趣。我想到了两者的结合(对于由于某种原因而跳过
finalize
的情况)。我无法从链接中理解-是否保证会出现意外终止?当然,正如您自己所说,这是不够的,因为我想在应用程序仍在运行时清理磁盘。我必须检查两种方法(
finalize
deleteOnExit
)此外,由于我觉得我没有得到关于它们的适当详细信息…您能更具体地说明一下
finalize
可能不被调用的情况吗?您认为这应该是我的一个真正的问题吗?在某些情况下,这并不是什么问题,只是您永远无法知道GC是否会在内存耗尽之前运行。finalize()任何给定对象的方法都可能运行,但您不能指望它,因此不要将任何必要的代码放入finalize()方法中。事实上,建议您一般不要覆盖finalize()。这是可能的,但我看不到任何好处。
deleteOnExit()
不会强制我维护已使用临时文件的列表(我更喜欢使用
File.createTempFile()
,以确保文件在某个时候会被操作系统删除,然后使用我自己的缓存)。此版本不是线程安全的。se
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);
    }
    ...
}