gpt4 book ai didi

java - 为什么我的对象不会死?

转载 作者:塔克拉玛干 更新时间:2023-11-03 05:13:50 25 4
gpt4 key购买 nike

我正在尝试实现一种机制,当保存缓存文件的对象死亡时删除缓存文件,并决定使用 PhantomReference 来获得有关对象垃圾回收的通知。问题是我一直遇到 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();
}
}
}

输出是:

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.

不用说,改变 sleep 时间,多次调用 gc 等都没有用。

更新

按照建议,我调用了我的引用的 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();
}
}

一些过滤后的输出:

2013-08-05 22:35:01,932 DEBUG 将图像保存到文件:<>\AppData\Local\Temp\tmp7..0.PNG

2013-08-05 22:35:03,379 DEBUG 删除未使用的文件:<>\AppData\Local\Temp\tmp7..0.PNG 创建于 2013 年 8 月 5 日星期一 22:35:02 IDT

最佳答案

答案是,在您的示例中,PhantomReference 本身是不可访问的,因此垃圾收集之前 引用的对象本身被垃圾收集。因此,在对象被 GC 时,不再有 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();
}
}
}

更新

在您的示例中可以手动调用 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();
}
}

输出将是这样的:

I'm born.
I'm dying!
working with item DeathNotificationObject@6908b095
stopped working with item DeathNotificationObject@6908b095

这意味着无论你想对引用队列做什么,都会在项目还活着的时候完成。

关于java - 为什么我的对象不会死?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/18065725/

25 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com