当实际对象被垃圾收集时,WeakHashMap中的条目中的值如何被垃圾收集?

时间:2018-06-04 18:45:10

标签: java collections garbage-collection weak-references weakhashmap

首先,我想阐明我对WeakReference的理解,因为以下问题依赖于同一个问题。

static void test() {
    Person p = new Person();
    WeakReference<Person> person = new WeakReference<>(p);
    p = null;
    System.gc();
    System.out.println(person.get());
    System.out.println(person);
}

static class Person {
    String name;
}

static class PersonMetadata {
    String someData;

    public PersonMetadata(String met) {
        someData = met;
    }
}

上述代码的输出是

null java.lang.ref.WeakReference@7852e922

这意味着虽然GC运行时实际的人物对象是垃圾收集,但是内存中有一个WeakReference<Person>类的对象,此时没有指向任何东西。

现在考虑到上述理解是真的,我对WeakHashMap<K,V>如何运作感到困惑。在下面的代码中

public static void main(String[] args) {
    Person p = new Person();
    p.name = "John";
    WeakHashMap<Person, PersonMetadata> map = new WeakHashMap<>();
    PersonMetadata meta = new PersonMetadata("Geek");
    map.put(p, meta);
    p = null;
    System.gc();
    if (map.values().contains(meta)) {
        System.out.println("Value present");
    } else {
        System.out.println("Value gone");
    }
}

static class Person {
    String name;
}

static class PersonMetadata {
    String someData;

    public PersonMetadata(String met) {
        someData = met;
    }
}

输出: Value gone

现在问题就是说WeakHashMap<K,V>中的密钥是弱引用,这意味着在上面的代码中 p 变为null实际对象可以被垃圾收集,因为没有更强的对象引用,但如何和PersonMetadata类的对象的值是如何得到垃圾收集,因为第一个代码证明即使收集了实际对象,WeakReference类的对象也不会被垃圾收集。

1 个答案:

答案 0 :(得分:2)

你误解了这种情况。当map.values().contains(meta)或短map.containsValue(meta)返回false时,并不意味着meta已被垃圾回收。实际上,您在meta中持有对该对象的引用,甚至将该引用传递给可能在其上调用contains的{​​{1}}方法。那么该对象如何被垃圾收集?

响应只告诉您,其中一个地图的密钥与该对象之间没有关联,因为唯一的密钥已经被垃圾收集,这是正确的答案。或者,您可以让equals检查是否存在关联。

这是WeakHashMap提供的内容:

  

基于哈希表的map.isEmpty()接口实现,带有弱键。如果Map中的条目不再正常使用,则会自动删除该条目。更准确地说,给定密钥的映射的存在不会阻止密钥被垃圾收集器丢弃,即,可以最终化,最终化,然后回收。当一个键被丢弃时,它的条目将被有效地从地图中删除,因此该类的行为与其他WeakHashMap实现的行为略有不同。

删除条目不是即时的。它依赖于将Map排入WeakReference,然后在您进行下一个查询时在内部进行轮询,例如ReferenceQueue甚至containsValue。例如。如果我将你的程序改为:

size()

它偶尔会打印“Value present”,尽管此时密钥Person p = new Person(); WeakHashMap<Person, PersonMetadata> map = new WeakHashMap<>(); PersonMetadata meta = new PersonMetadata("Geek"); map.put(p, meta); WeakReference<?> ref = new WeakReference<>(p); p = null; while(ref.get() != null) System.gc(); System.out.println(map.containsValue(meta)? "Value present": "Value gone"); 实例已被垃圾收集。如上所述,这是关于地图的内部清理,而不是我们在Person中持有强引用的PersonMetadata实例。

使meta有资格进行垃圾回收是完全不同的事情。如上所述,只要我们调用方法,PersonMetadata就会进行内部清理。如果我们不这样做,即使密钥已被垃圾收集,也不会进行清理,因此仍然是一个强有力的参考。考虑:

WeakReference

将打印

Person p = new Person();
WeakHashMap<Person, PersonMetadata> map = new WeakHashMap<>();
PersonMetadata meta = new PersonMetadata("Geek");
map.put(p, meta);
WeakReference<?> personRef = new WeakReference<>(p);
WeakReference<?> metaRef = new WeakReference<>(meta);
p = null;
meta = null;
while(personRef.get() != null) System.gc();
System.out.println("Person collected");
for(int i = 0; metaRef.get() != null && i < 10; i++) {
    System.out.println("PersonMetadata not collected");
    System.gc();
    Thread.sleep(1000);
}
System.out.println("calling a query method on map");
System.out.println("map.size() == "+map.size());
System.gc();
System.out.println("PersonMetadata "+(metaRef.get()==null? "collected": "not collected"));

演示Person collected PersonMetadata not collected PersonMetadata not collected PersonMetadata not collected PersonMetadata not collected PersonMetadata not collected PersonMetadata not collected PersonMetadata not collected PersonMetadata not collected PersonMetadata not collected PersonMetadata not collected calling a query method on map map.size() == 0 PersonMetadata collected 如何对已经收集的键的值进行强引用,直到我们最终调用它的方法为止,让它有机会执行其内部清理。

WeakHashMap和我们的方法都没有对它进行引用时,最终会收集该值。当我们删除WeakHashMap语句时,地图在结尾处(在其内部清理之后)仍然是空的,但不会收集该值。

重要的是要记住,这些代码示例仅用于演示目的并触及特定于实现的行为,最值得注意的是,meta = null;方法通常未经优化运行。形式上,如果对象未被使用,则不需要局部变量来防止垃圾收集,这一点在方法优化时与实践相关。