如何在被收集时获取java对象本身 [英] How to get java object itself when its about to be collected

查看:117
本文介绍了如何在被收集时获取java对象本身的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

如果我们无法控制它的源代码(不能强制执行某个接口或最终阻止),我们如何在收集之前使用对象执行一段代码(其状态是否需要)?



Java引用类型允许我们访问一个对象,如果其他人使其可访问+如果我们使用引用队列,我们​​也可以在收集对象时收到通知,除非我的理解错误,那就是你可以用引用类型完成的所有事情,不管你在任何时候使用什么对象都是可达到的或者它已经消失,并且你有空。



我真正需要的是一种在特定对象即将被收集时得到通知的方式。 解决方案

有一个原因Reference API不允许检索收集的对象:允许再次获取收集到的对象,就像 finalize()方法所发生的那样,这正是没有打算的



标准方法是创建参考类型的子类以存储与所指对象相关的信息,例如,在专门的参考对象内执行清理操作所需的一切。当然,这些信息不能包含对引用本身的强引用。

  private static final ReferenceQueue< Integer> QUEUE = new ReferenceQueue<>(); 
static class IntegerPhantomReference extends PhantomReference< Integer> {
final int value;
public IntegerPhantomReference(Integer ref){
super(ref,QUEUE);
value = ref.intValue();
}
public String toString(){
returnInteger [value =+ value +];
}
}
private static final Set< IntegerPhantomReference> REGISTERED = new HashSet<>();
public static void main(String [] args)throws InterruptedException {
List< Integer> strongReferenced = new ArrayList<>();
for(int i = 0; i <10; i ++){
Integer object = new Integer(i);
stronglyReferenced.add(object);
REGISTERED.add(new IntegerPhantomReference(object));
}
gcAndPoll(initial);
stronglyReferenced.removeIf(i - > i%2 == 0);
gcAndPoll(删除后);
stronglyReferenced.clear();
gcAndPoll(全部删除后);
if(REGISTERED.isEmpty())System.out.println(收集的所有对象);

private static void gcAndPoll(String msg)throws InterruptedException {
System.out.println(msg);
System.gc();了Thread.sleep(100); (b; b)(
参考<?> r = QUEUE.poll();
if(r == null)break;
System.out.println(collect+ r);
REGISTERED.remove(r);




 初始
删除后甚至
收集整数[值= 4]
收集整数[值= 8]
收集整数[值= 6]
收集整数[值= 2]
收集整数[值= 0]
删除所有
后收集整数[值= 1]
收集整数[值= 5]
收集整数[值= 3]
收集整数[值= 7]
收集整数[值= 9]
收集的所有对象
pre>




为了保证完整性,有一种黑客允许重新生成一个收集到的对象,它将在Java 9中停止工作。

PhantomReference 表示:


与软弱引用不同,幻影引用不会被垃圾收集器自动清除因为它们已入队。


目前尚不清楚为什么这已被指定,并且 get()方法 PhantomReference 已被覆盖,始终返回 null ,完全不允许从以下事实中获益:该参考文件尚未被清除。由于此特殊行为的目的不明确,因此它已从Java 9中的规范中删除,并且这些引用将自动像其他任何清除一样清除。



但对于以前的版本,可以使用带访问覆盖的反射来访问指示对象,以准确执行API不允许的内容。不用说,这仅仅是为了提供信息的目的,并且强烈不鼓励(并且如所述,它在Java 9中停止工作)。

  private static final ReferenceQueue< Integer> QUEUE = new ReferenceQueue<>(); 
private static final Set< PhantomReference< Integer>> REGISTERED = new HashSet<>();
public static void main(String [] args)
throws InterruptedException,IllegalAccessException {
List< Integer> strongReferenced = new ArrayList<>();
for(int i = 0; i <10; i ++){
Integer object = new Integer(i);
stronglyReferenced.add(object);
REGISTERED.add(new PhantomReference<>(object,QUEUE));
}
gcAndPoll(initial);
stronglyReferenced.removeIf(i - > i%2 == 0);
gcAndPoll(删除后);
stronglyReferenced.clear();
gcAndPoll(全部删除后);
if(REGISTERED.isEmpty())System.out.println(收集的所有对象);
}
static final字段REFERENT;
static {
try {
REFERENT = Reference.class.getDeclaredField(referent);
REFERENT.setAccessible(true);
} catch(NoSuchFieldException ex){
throw new ExceptionInInitializerError(ex);


private static void gcAndPoll(String msg)
throws InterruptedException,IllegalAccessException {
System.out.println(msg);
System.gc();
Thread.sleep(100); (b; b)(
参考<?> r = QUEUE.poll();
if(r == null)break;
Object o = REFERENT.get(r);
System.out.println(收集(现在复活)+ o);
REGISTERED.remove(r);
}
}


How can we execute a piece of code using the object (its state is needed) before it gets collected if we don't have control over its source (cant enforce implementing some interface or finally block)?

Java Reference types allow us to access an object if someone else makes it strongly reachable + if we use reference queues we can also be notified when the object is collected, unless my understanding is wrong that's all you can do with reference types, no matter what you use at any point the object is either strongly reachable or its gone and you have null.

All i really need is a way to get notified when specific object is about to be collected.

解决方案

There is a reason why the Reference API doesn’t allow to retrieve the collected object: allowing to make a collected object reachable again, like happening with the finalize() method, is exactly what is not intended.

The standard approach is to create subclasses of the reference types to store the information associated with the referent, e.g. everything necessary to perform the cleanup action, within the specialized reference object. Of course, this information must not include strong references to the referent itself.

private static final ReferenceQueue<Integer> QUEUE = new ReferenceQueue<>();
static class IntegerPhantomReference extends PhantomReference<Integer> {
    final int value;
    public IntegerPhantomReference(Integer ref) {
        super(ref, QUEUE);
        value = ref.intValue();
    }
    public String toString() {
        return "Integer[value="+value+"]";
    }
}
private static final Set<IntegerPhantomReference> REGISTERED = new HashSet<>();
public static void main(String[] args) throws InterruptedException {
    List<Integer> stronglyReferenced = new ArrayList<>();
    for(int i = 0; i < 10; i++) {
        Integer object = new Integer(i);
        stronglyReferenced.add(object);
        REGISTERED.add(new IntegerPhantomReference(object));
    }
    gcAndPoll("initial");
    stronglyReferenced.removeIf(i -> i%2 == 0);
    gcAndPoll("after removing even");
    stronglyReferenced.clear();
    gcAndPoll("after remove all");
    if(REGISTERED.isEmpty()) System.out.println("all objects collected");
}
private static void gcAndPoll(String msg) throws InterruptedException {
    System.out.println(msg);
    System.gc(); Thread.sleep(100);
    for(;;) {
        Reference<?> r = QUEUE.poll();
        if(r == null) break;
        System.out.println("collected "+r);
        REGISTERED.remove(r);
    }
}

initial
after removing even
collected Integer[value=4]
collected Integer[value=8]
collected Integer[value=6]
collected Integer[value=2]
collected Integer[value=0]
after remove all
collected Integer[value=1]
collected Integer[value=5]
collected Integer[value=3]
collected Integer[value=7]
collected Integer[value=9]
all objects collected


For completeness, there is a hack that allows to resurrect a collected object, which will stop working in Java 9.

The documentation of PhantomReference says:

Unlike soft and weak references, phantom references are not automatically cleared by the garbage collector as they are enqueued.

It’s not clear why this has been specified and the get() method of PhantomReference has been overridden to always return null, exactly to disallow taking any benefit from the fact that this reference has not been cleared. Since the purpose of this special behavior is unclear, it has been removed from the specification in Java 9 and these references are automatically cleared like any other.

But for previous versions, it is possible to use Reflection with access override to access the referent, to do exactly what the API was not intended to allow. Needless to say, that’s just for informational purpose and is strongly discouraged (and as said, it stops working in Java 9).

private static final ReferenceQueue<Integer> QUEUE = new ReferenceQueue<>();
private static final Set<PhantomReference<Integer>> REGISTERED = new HashSet<>();
public static void main(String[] args)
        throws InterruptedException, IllegalAccessException {
    List<Integer> stronglyReferenced = new ArrayList<>();
    for(int i = 0; i < 10; i++) {
        Integer object = new Integer(i);
        stronglyReferenced.add(object);
        REGISTERED.add(new PhantomReference<>(object, QUEUE));
    }
    gcAndPoll("initial");
    stronglyReferenced.removeIf(i -> i%2 == 0);
    gcAndPoll("after removing even");
    stronglyReferenced.clear();
    gcAndPoll("after remove all");
    if(REGISTERED.isEmpty()) System.out.println("all objects collected");
}
static final Field REFERENT;
static {
    try {
        REFERENT = Reference.class.getDeclaredField("referent");
        REFERENT.setAccessible(true);
    } catch (NoSuchFieldException ex) {
        throw new ExceptionInInitializerError(ex);
    }
}
private static void gcAndPoll(String msg)
        throws InterruptedException, IllegalAccessException {
    System.out.println(msg);
    System.gc();
    Thread.sleep(100);
    for(;;) {
        Reference<?> r = QUEUE.poll();
        if(r == null) break;
        Object o = REFERENT.get(r);
        System.out.println("collected (and now resurrected)"+o);
        REGISTERED.remove(r);
    }
}

这篇关于如何在被收集时获取java对象本身的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

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