具体的用例:对于二进制数据,它被广泛用于处理任意大小的二进制斑点的抽象。 由于抽象未经创建尽管有关VM 以外的东西,现有的实现依赖于垃圾收集器的生命周期。
现在我想补充一点,采用离堆存储一个新的实现(例如,在一个临时文件)。 由于有大量的现有代码,使用抽象,引入附加的方法明确的生命周期管理是不切实际的,我不能重写使用,以确保他们所管理的新的生命周期的每一个要求客户使用情况。
我能想到的两种解决方法,但不能决定哪一个更好:
一。)使用的finalize()来管理相关资源的生命周期(如临时文件会被敲定删除。这似乎很容易实现。
湾)的参考队列的使用和java.lang.Reference(但哪一个,弱或与删除该文件,当参考排入一些额外的物体影像?)。 这似乎有点更多的工作,我需要创建不仅是新的实现,但分离出其清理数据,并确保清理对象不能是已经暴露给用户的对象之前GC'd 。
角)其他一些方法我没有,虽然吗?
我应该采取哪一种方法(和我为什么要喜欢它)? 实施提示也欢迎。
编辑:需要reliaility的程度-我的目的了完全正常的,如果万一临时文件未清理虚拟机突然终止。 主要关注的是,虽然虚拟机运行时,它可以很好地填补了本地磁盘(超过几天的过程中)与临时文件(这发生在我身上的真实与Apache TIKA,提取文本时创建的临时文件从某些文档类型,zip文件是我相信的罪魁祸首)。 我已安排在机器上定期清理,所以如果一个文件删除由清理并不意味着世界末日 - 只要它不会在很短的时间间隔定期发生。
至于我能决定的finalize()的工作原理与Oracale JRE。 如果我正确地解释的javadoc,参考文献必须作为记录(有没有办法仅轻声/弱可参考对象不OutOfMemoryError异常清除之前抛出)。 这意味着当VM可以决定不回收特定对象的很长一段时间,它有这样做的最新当堆得到充分。 反过来,这意味着只能存在于堆上我的基于文件的斑点的数量有限。 VM具有清除它们在某些时候,或者它会确定耗尽内存。 还是有它允许虚拟机运行不清除引用OOM任何漏洞(假设他们没有stronly refered了)?
EDIT2:据我看到它在这一点上双方敲定()和参考应该是足够可靠的,我的目的,但我收集参考可能是更好的解决方案,因为它与GC相互作用不能复活死亡的对象,因此它的性能影响要少吗?
EDIT3:依靠VM终止或启动(关闭挂钩或类似)解决方案的方法是不是对我有用处,因为通常在虚拟机运行的时间过长(服务器环境)。
下面是有效的Java相关的项目: 避免终结
该项目内包含的是怎么做@delnan建议在评论的建议: 提供一个明确的终止方法 。 提供以及大量的例子: InputStream.close()
Graphics.dispose()
等处了解到母牛可能已经留在一个谷仓...
无论如何,这里有一个如何这可能参照对象来完成的草图。 首先,对于二进制数据的接口:
import java.io.IOException;
public interface Blob {
public byte[] read() throws IOException;
public void update(byte[] data) throws IOException;
}
接下来,基于文件的实现:
import java.io.File;
import java.io.IOException;
public class FileBlob implements Blob {
private final File file;
public FileBlob(File file) {
super();
this.file = file;
}
@Override
public byte[] read() throws IOException {
throw new UnsupportedOperationException();
}
@Override
public void update(byte[] data) throws IOException {
throw new UnsupportedOperationException();
}
}
然后,一个工厂来创建和跟踪基于文件的斑点:
import java.io.File;
import java.io.IOException;
import java.lang.ref.PhantomReference;
import java.lang.ref.Reference;
import java.lang.ref.ReferenceQueue;
import java.util.Timer;
import java.util.TimerTask;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ConcurrentMap;
public class FileBlobFactory {
private static final long TIMER_PERIOD_MS = 10000;
private final ReferenceQueue<File> queue;
private final ConcurrentMap<PhantomReference<File>, String> refs;
private final Timer reaperTimer;
public FileBlobFactory() {
super();
this.queue = new ReferenceQueue<File>();
this.refs = new ConcurrentHashMap<PhantomReference<File>, String>();
this.reaperTimer = new Timer("FileBlob reaper timer", true);
this.reaperTimer.scheduleAtFixedRate(new FileBlobReaper(), TIMER_PERIOD_MS, TIMER_PERIOD_MS);
}
public Blob create() throws IOException {
File blobFile = File.createTempFile("blob", null);
//blobFile.deleteOnExit();
String blobFilePath = blobFile.getCanonicalPath();
FileBlob blob = new FileBlob(blobFile);
this.refs.put(new PhantomReference<File>(blobFile, this.queue), blobFilePath);
return blob;
}
public void shutdown() {
this.reaperTimer.cancel();
}
private class FileBlobReaper extends TimerTask {
@Override
public void run() {
System.out.println("FileBlob reaper task begin");
Reference<? extends File> ref = FileBlobFactory.this.queue.poll();
while (ref != null) {
String blobFilePath = FileBlobFactory.this.refs.remove(ref);
File blobFile = new File(blobFilePath);
boolean isDeleted = blobFile.delete();
System.out.println("FileBlob reaper deleted " + blobFile + ": " + isDeleted);
ref = FileBlobFactory.this.queue.poll();
}
System.out.println("FileBlob reaper task end");
}
}
}
最后,测试,其中包括一些人工GC“压力”来得到的东西去:
import java.io.IOException;
public class FileBlobTest {
public static void main(String[] args) {
FileBlobFactory factory = new FileBlobFactory();
for (int i = 0; i < 10; i++) {
try {
factory.create();
} catch (IOException exc) {
exc.printStackTrace();
}
}
while(true) {
try {
Thread.sleep(5000);
System.gc(); System.gc(); System.gc();
} catch (InterruptedException exc) {
exc.printStackTrace();
System.exit(1);
}
}
}
}
这应该产生一些输出,如:
FileBlob reaper task begin
FileBlob reaper deleted C:\WINDOWS\Temp\blob1055430495823649476.tmp: true
FileBlob reaper deleted C:\WINDOWS\Temp\blob873625122345395275.tmp: true
FileBlob reaper deleted C:\WINDOWS\Temp\blob4123088770942737465.tmp: true
FileBlob reaper deleted C:\WINDOWS\Temp\blob1631534546278785404.tmp: true
FileBlob reaper deleted C:\WINDOWS\Temp\blob6150533076250997032.tmp: true
FileBlob reaper deleted C:\WINDOWS\Temp\blob7075872276085608840.tmp: true
FileBlob reaper deleted C:\WINDOWS\Temp\blob5998579368597938203.tmp: true
FileBlob reaper deleted C:\WINDOWS\Temp\blob3779536278201681316.tmp: true
FileBlob reaper deleted C:\WINDOWS\Temp\blob8720399798060613253.tmp: true
FileBlob reaper deleted C:\WINDOWS\Temp\blob3046359448721598425.tmp: true
FileBlob reaper task end
这是我kschneids基于参考实例后炮制的解决方案(万一有人需要一个一般可用的实现)。 它的记录,应该很容易理解/适应:
import java.lang.ref.PhantomReference;
import java.lang.ref.Reference;
import java.lang.ref.ReferenceQueue;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
/**
* Helper class for cleaning up resources when an object is
* garbage collected. Use as follows (both anonymous subclass or
* public subclass are fine. Be extra careful to not retain
* a reference to the trigger!):
*
* new ResourceFinalizer(trigger) {
*
* // put user defined state relevant for cleanup here
*
* protected void cleanup() {
* // implement cleanup procedure.
* }
* }
*
* Typical application is closing of native resources when an object
* is garbage collected (e.g. VM external resources).
*
* You must not retain any references from the ResourceFinalizer to the
* trigger (otherwise the trigger can never become eligible for GC).
* You can however retain references to the ResourceFinalizer from the
* trigger, so you can access the data relevant for the finalizer
* from the trigger (no need to duplicate the data).
* There is no need to explicitly reference the finalizer after it has
* been created, the finalizer base class will ensure the finalizer
* itself is not eligible for GC until it has been run.
*
* When the VM terminates, ResourceFinalizer that haven't been
* triggered will run, regardless of the state of their triggers
* (that is even if the triggers are still reachable, the finalizer
* will be called). There are no guarantees on this, if the VM
* is terminated abruptly this step may not take place.
*/
public abstract class ResourceFinalizer {
/**
* Constructs a ResourceFinalizer that is triggered when the
* object referenced by finalizationTrigger is garbage collected.
*
* To make this work, you must ensure there are no references to
* the finalizationTrigger object from the ResourceFinalizer.
*/
protected ResourceFinalizer(final Object trigger) {
// create reference to trigger and register this finalizer
final Reference<Object> reference = new PhantomReference<Object>(trigger, referenceQueue);
synchronized (finalizerMap) {
finalizerMap.put(reference, this);
}
}
/**
* The cleanup() method is called when the finalizationTrigger
* has been garbage collected.
*/
protected abstract void cleanup();
// --------------------------------------------------------------
// ---
// --- Background finalization management
// ---
// --------------------------------------------------------------
/**
* The reference queue used to interact with the garbage collector.
*/
private final static ReferenceQueue<Object> referenceQueue = new ReferenceQueue<Object>();
/**
* Global static map of finalizers. Enqueued references are used as key
* to find the finalizer for the referent.
*/
private final static HashMap<Reference<?>, ResourceFinalizer> finalizerMap =
new HashMap<Reference<?>, ResourceFinalizer>(16, 2F);
static {
// create and start finalizer thread
final Thread mainLoop = new Thread(new Runnable() {
@Override
public void run() {
finalizerMainLoop();
}
}, "ResourceFinalizer");
mainLoop.setDaemon(true);
mainLoop.setPriority(Thread.NORM_PRIORITY + 1);
mainLoop.start();
// add a shutdown hook to take care of resources when the VM terminates
final Thread shutdownHook = new Thread(new Runnable() {
@Override
public void run() {
shutdownHook();
}
});
Runtime.getRuntime().addShutdownHook(shutdownHook);
}
/**
* Main loop that runs permanently and executes the finalizers for
* each object that has been garbage collected.
*/
private static void finalizerMainLoop() {
while (true) {
final Reference<?> reference;
try {
reference = referenceQueue.remove();
} catch (final InterruptedException e) {
// this will terminate the thread, should never happen
throw new RuntimeException(e);
}
final ResourceFinalizer finalizer;
// find the finalizer for the reference
synchronized (finalizerMap) {
finalizer = finalizerMap.remove(reference);
}
// run the finalizer
callFinalizer(finalizer);
}
}
/**
* Called when the VM shuts down normally. Takes care of calling
* all finalizers that haven't been triggered yet.
*/
private static void shutdownHook() {
// get all remaining resource finalizers
final List<ResourceFinalizer> remaining;
synchronized (finalizerMap) {
remaining = new ArrayList<ResourceFinalizer>(finalizerMap.values());
finalizerMap.clear();
}
// call all remaining finalizers
for (final ResourceFinalizer finalizer : remaining) {
callFinalizer(finalizer);
}
}
private static void callFinalizer(final ResourceFinalizer finalizer) {
try {
finalizer.cleanup();
} catch (final Exception e) {
// don't care if a finalizer throws
}
}
}
如果你不是特别担心很快清理的文件,然后finalize
是要走的路。 谁也不能保证任何特定的对象是有史以来GC'd,即使您运行的内存(VM在理论上可以只收集堆的一部分)。 但是,如果一个对象被GC'd它将被最终确定,所以你知道你将拥有最多的sizeof(堆)/的sizeof(内存句柄)未确定的斑点,这使一些约束你的磁盘使用情况。 这是一个相当薄弱的束缚,但它听起来像它可能对你不够好。
在紧要关头,只是做在你的终结是个不错的解决方案,将至少关闭您的文件都有一个良好的比例,大概。 如果这是够好,我会走这条路线,因为它会容易得多。
在另一方面,如果你正在寻找,然后使用终结任何肯定很不好; 你不能依赖于它们正在不断运行,更别说及时,而这同样的道理也适用更松散的各种特殊类型的参考过清理。 它,而取决于你的应用程序,硬件的详细信息,但一般而言,您有没有保证之前,你的硬盘填补了文献将被清除。
这是更有可能发生,如果你在内存中保存(这是考虑大部分空间)的数据是巨大的,但非常短暂的,而文件引用持续更长的时间。 这导致大量的小型垃圾回收,这将清除年轻一代的空间,删除死的数据,并最终促使许多文件引用的,但结果没有大的垃圾回收,这将清除旧的终身的对象,如你的文件引用,所以这些都是然后保持活力无限。 看看这更多的GC背景。 您也许能够提高多少您的终结实际上得到通过提高年轻一代的尺寸,以换取稍微慢一些选区的打击。
如果你想要更多的确定性,我想解决这个问题略有不同。 首先,实施终结清理作为一个快速简单的情况下的解决方案。 然后建立一个后备过; 决定你是准备让你的文件占用,比你想到居然使用,监视你使用每隔X分钟的总空间优选基本上更多的空间最大数量,如果越过这势必然后删除选择最老的(由上次写入时间)的文件,例如最早的10%。 这给你一个相当辛苦的上限,你可能可以保持检查频率非常低的位置,因为终结应该有希望赶上大多数问题。
另外一个说明,我认为可能是半相关的是deleteOnExit 。 当他们正在创建将保证他们将被自动删除当JVM成功退出对临时文件调用此。 这确实有缺点 ,但:在JVM必须坚持这个文件的引用,直到它关闭时,它给你留下一个小的内存泄漏(每个文件1K,我相信)。 不知道这是否使得它值得你,但可能会帮助!
文章来源: Reliable method of cleaning up an external resource associated with an Object