为什么我的物体不会死? [英] Why won't my objects die?

查看:147
本文介绍了为什么我的物体不会死?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我试图实现一种机制,在持有它们的对象死亡时删除缓存的文件,并决定使用 PhantomReference 来获取垃圾回收通知目的。问题是我经常遇到 ReferenceQueue 的奇怪行为。当我更改代码中的某些内容时,它突然不再提取对象。所以我试着让这个例子进行测试,并遇到同样的问题:

  public class DeathNotificationObject {
private static ReferenceQueue< DeathNotificationObject>
refQueue = new ReferenceQueue< DeathNotificationObject>();

static {
线程deathThread = new Thread(死亡通知){
@Override
public void run(){
try {
while(true){
refQueue.remove();
System.out.println(我快死了!);
}
} catch(Throwable t){
t.printStackTrace();
}
}
};
deathThread.setDaemon(true);
deathThread.start();
}

public DeathNotificationObject(){
System.out.println(我出生。);
new PhantomReference< DeathNotificationObject>(this,refQueue);


(int i = 0; i <10; i ++){
new DeathNotificationObject($ {

$ b public static void main );
}
尝试{
System.gc();
Thread.sleep(3000);
} catch(InterruptedException e){
e.printStackTrace();
}
}
}

输出结果为: p>

 我出生了。 
我出生了。
我出生了。
我出生了。
我出生了。
我出生了。
我出生了。
我出生了。
我出生了。
我出生了。

不用说,更改睡眠时间时间,调用 gc 多次等都无效。


$ b

更新 p>

如上所述,我调用了我的引用的 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 {
线程cleanUpThread =新线程(图像临时文件清理){
@Override
public void run(){
try {
while(true){
参考<?扩展ElementCachedImage> phanRef =
refQue.remove();
文件f = refMap.remove(phanRef);
日历c = Calendar.getInstance();
c.setTimeInMillis(f.lastModified());
_log.debug(删除在+ c.getTime()创建的未使用的文件:+ f +);
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);
返回false;
}
返回true;
}

public ElementCachedImage(BufferedImage bi){
if(bi == null)throw new NullPointerException();
img = new ImageWrapper(bi);
PhantomReference< ElementCachedImage> pref =
新PhantomReference< ElementCachedImage>(this,refQue);
refMap.put(pref,img.getFile());
$ b $ new线程(保存图像到文件){
@Override
public void run(){
synchronized(ElementCachedImage.this){
if(img!= null){
img.saveToFile();
img.getFile()。deleteOnExit();
}
}
}
} .start();


$ / code>

一些过滤后的输出:

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 :35:02 IDT 2013

解决方案

答案是,在你的例子中 PhantomReference 本身是无法访问的,因此在被引用的对象本身被垃圾收集之前收集了垃圾。因此,在对象被GCed时,不再有 Reference ,并且GC不知道它应该排入某处。


$ b $这当然是一种头对头的比赛: - )

这也解释了(没有深入研究你的新代码)为什么要把对于某些可访问的集合的引用使得该示例有效。



仅供参考(双关语意)此处是您的第一个示例的修改版本(适用于我的机器: - )我刚刚添加了一个包含所有引用的集合。

  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 {
线程deathThread = new Thread(死亡通知){
@Override
public void run(){
try {
while(true){
参考<?扩展DeathNotificationObject> ref = refQueue.remove();
refs.remove(ref);
System.out.println(我快死了!);
}
} catch(Throwable t){
t.printStackTrace();
}
}
};
deathThread.setDaemon(true);
deathThread.start();
}

public DeathNotificationObject(){
System.out.println(我出生。);
PhantomReference< DeathNotificationObject> ref = new PhantomReference< DeathNotificationObject>(this,refQueue);
refs.add(ref);


(int i = 0; i <10; i ++){
new DeathNotificationObject($ {

$ b public static void main );
}
尝试{
System.gc();
Thread.sleep(3000);
} catch(InterruptedException e){
e.printStackTrace();



code
$ b $ hr

更新



手动调用 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(使用item+ item);
Thread.sleep(1000);
System.out.println(停止使用item +项目);
//模拟版本项目
item = null;
}

尝试{
System.gc();
Thread.sleep(3000);
} catch(InterruptedException e){
e.printStackTrace();


code
$ b

输出结果如下:

 我出生了。 
我快死了!
使用item DeathNotificationObject @ 6908b095
停止使用item DeathNotificationObject @ 6908b095

这意味着,无论你想用引用队列做什么,都会在物品还活着时完成。


I'm trying to implement a mechanism that deletes cached files when the objects that hold them die, and decided to use PhantomReferences to get notified on garbage collection of an object. The problem is I keep experiencing weird behavior of the ReferenceQueue. When I change something in my code it suddenly doesn't fetch objects anymore. So I tried to make this example for testing, and ran into the same problem:

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

The output is:

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.

Needless to say, changing the sleep time, calling gc multiple times etc. didn't work.

UPDATE

As suggested, I called Reference.enqueue() of my reference, which solved the problem.

The weird thing, is that I have some code that works perfectly (just tested it), although it never calls enqueue. Is it possible that putting the Reference into a Map somehow magically enqueued the reference?

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

Some filtered output:

2013-08-05 22:35:01,932 DEBUG Save image to file: <>\AppData\Local\Temp\tmp7..0.PNG

2013-08-05 22:35:03,379 DEBUG Deleting unused file: <>\AppData\Local\Temp\tmp7..0.PNG created at Mon Aug 05 22:35:02 IDT 2013

解决方案

The answer is, that in your example the PhantomReference itself is unreachable and hence garbage collected before the referred object itself is garbage collected. So at the time the object is GCed there is no more Reference and the GC does not know that it should enqueue something somewhere.

This of course is some kind of head-to-head race :-)

This also explains (without looking to deep into your new code) why putting the reference into some reachable collection makes the example work.

Just for reference (pun intended) here is a modified version of your first example which works (on my machine :-) I just added a set holding all references.

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


Update

Calling enqueue by hand is possible in your example but not in real code. it gives plain wrong result. Let me show by calling enqueue in the constructor and using another 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();
    }
}

The output will be like this:

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

Which means that whatever you wanted to do with the reference queue would be done when the item is still alive.

这篇关于为什么我的物体不会死?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆