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);
}
...
}