Eli*_*ist 6 java garbage-collection phantom-reference
我正在尝试实现一种机制,当持有它们的对象死亡时删除缓存的文件,并决定使用PhantomReference
s来获取有关对象的垃圾收集的通知.问题是我一直在经历着奇怪的行为ReferenceQueue
.当我在代码中更改某些内容时,它突然不再获取对象.所以我试着让这个例子进行测试,并遇到了同样的问题:
public class DeathNotificationObject {
private static ReferenceQueue<DeathNotificationObject>
refQueue = new ReferenceQueue<DeathNotificationObject>();
static {
Thread deathThread = new Thread("Death notification") {
@Override
public void run() {
try {
while (true) {
refQueue.remove();
System.out.println("I'm dying!");
}
} catch (Throwable t) {
t.printStackTrace();
}
}
};
deathThread.setDaemon(true);
deathThread.start();
}
public DeathNotificationObject() {
System.out.println("I'm born.");
new PhantomReference<DeathNotificationObject>(this, refQueue);
}
public static void main(String[] args) {
for (int i = 0 ; i < 10 ; i++) {
new DeathNotificationObject();
}
try {
System.gc();
Thread.sleep(3000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
Run Code Online (Sandbox Code Playgroud)
输出是:
I'm born.
I'm born.
I'm born.
I'm born.
I'm born.
I'm born.
I'm born.
I'm born.
I'm born.
I'm born.
Run Code Online (Sandbox Code Playgroud)
不用说,改变sleep
时间,gc
多次呼叫等都不起作用.
UPDATE
正如所建议的,我打电话Reference.enqueue()
给我的参考资料,它解决了这个问题.
奇怪的是,我有一些完美的代码(只是测试它),虽然它从来没有调用过enqueue
.是否有可能Reference
以Map
某种方式神奇地将该引用排成一行?
public class ElementCachedImage {
private static Map<PhantomReference<ElementCachedImage>, File>
refMap = new HashMap<PhantomReference<ElementCachedImage>, File>();
private static ReferenceQueue<ElementCachedImage>
refQue = new ReferenceQueue<ElementCachedImage>();
static {
Thread cleanUpThread = new Thread("Image Temporary Files cleanup") {
@Override
public void run() {
try {
while (true) {
Reference<? extends ElementCachedImage> phanRef =
refQue.remove();
File f = refMap.remove(phanRef);
Calendar c = Calendar.getInstance();
c.setTimeInMillis(f.lastModified());
_log.debug("Deleting unused file: " + f + " created at " + c.getTime());
f.delete();
}
} catch (Throwable t) {
_log.error(t);
}
}
};
cleanUpThread.setDaemon(true);
cleanUpThread.start();
}
ImageWrapper img = null;
private static Logger _log = Logger.getLogger(ElementCachedImage.class);
public boolean copyToFile(File dest) {
try {
FileUtils.copyFile(img.getFile(), dest);
} catch (IOException e) {
_log.error(e);
return false;
}
return true;
}
public ElementCachedImage(BufferedImage bi) {
if (bi == null) throw new NullPointerException();
img = new ImageWrapper(bi);
PhantomReference<ElementCachedImage> pref =
new PhantomReference<ElementCachedImage>(this, refQue);
refMap.put(pref, img.getFile());
new Thread("Save image to file") {
@Override
public void run() {
synchronized(ElementCachedImage.this) {
if (img != null) {
img.saveToFile();
img.getFile().deleteOnExit();
}
}
}
}.start();
}
}
Run Code Online (Sandbox Code Playgroud)
一些过滤输出:
2013-08-05 22:35:01,932 DEBUG将图像保存到文件:<>\AppData\Local\Temp\tmp7..0.PNG
2013-08-05 22:35:03,379调试删除未使用的文件:<>\AppData\Local\Temp\tmp7..0.PNG创建时间:Aug Aug 05 22:35:02 IDT 2013
答案是,在您的示例中,PhantomReference
本身无法访问,因此在引用对象本身被垃圾收集之前会收集垃圾.因此,当对象被GCed时,没有更多Reference
,并且GC不知道它应该在某处排队.
这当然是某种头对头的比赛:-)
这也解释了(不深入研究新代码)为什么将引用放入一些可访问的集合使得示例工作.
仅供参考(双关语)这里是你的第一个例子的修改版本(在我的机器上:-)我刚添加了一个包含所有引用的集合.
import java.lang.ref.PhantomReference;
import java.lang.ref.Reference;
import java.lang.ref.ReferenceQueue;
import java.util.HashSet;
import java.util.Set;
public class DeathNotificationObject {
private static ReferenceQueue<DeathNotificationObject> refQueue = new ReferenceQueue<DeathNotificationObject>();
private static Set<Reference<DeathNotificationObject>> refs = new HashSet<>();
static {
Thread deathThread = new Thread("Death notification") {
@Override
public void run() {
try {
while (true) {
Reference<? extends DeathNotificationObject> ref = refQueue.remove();
refs.remove(ref);
System.out.println("I'm dying!");
}
} catch (Throwable t) {
t.printStackTrace();
}
}
};
deathThread.setDaemon(true);
deathThread.start();
}
public DeathNotificationObject() {
System.out.println("I'm born.");
PhantomReference<DeathNotificationObject> ref = new PhantomReference<DeathNotificationObject>(this, refQueue);
refs.add(ref);
}
public static void main(String[] args) {
for (int i = 0 ; i < 10 ; i++) {
new DeathNotificationObject();
}
try {
System.gc();
Thread.sleep(3000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
Run Code Online (Sandbox Code Playgroud)
更新
enqueue
在您的示例中可以手动调用,但不能在实际代码中调用.它给出了错误的结果.让我通过调用enqueue
构造函数并使用另一个来显示main
:
public DeathNotificationObject() {
System.out.println("I'm born.");
PhantomReference<DeathNotificationObject> ref = new PhantomReference<DeathNotificationObject>(this, refQueue);
ref.enqueue();
}
public static void main(String[] args) throws InterruptedException {
for (int i = 0 ; i < 5 ; i++) {
DeathNotificationObject item = new DeathNotificationObject();
System.out.println("working with item "+item);
Thread.sleep(1000);
System.out.println("stopped working with item "+item);
// simulate release item
item = null;
}
try {
System.gc();
Thread.sleep(3000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
Run Code Online (Sandbox Code Playgroud)
输出将如下所示:
I'm born.
I'm dying!
working with item DeathNotificationObject@6908b095
stopped working with item DeathNotificationObject@6908b095
Run Code Online (Sandbox Code Playgroud)
这意味着当项目仍处于活动状态时,无论您想要如何处理引用队列.