当前位置: 首页 > 面试题库 >

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

楮庆
2023-03-14
问题内容

首先,我想澄清一下我的理解,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即使收集了实际的对象,该类的对象也未被垃圾回收。


问题答案:

您误会了情况。当map.values().contains(meta)或short
map.containsValue(meta)返回时false,并不表示meta已被垃圾回收。实际上,您在其中持有对该对象的引用meta,甚至将该引用传递给contains可能equals在其上调用的方法。那么该对象如何被垃圾回收?

该响应仅告诉您映射的键之一与该对象之间没有关联,并且由于唯一的键已被垃圾回收,因此这是正确的答案。或者,您可能只是要求map.isEmpty()检查关联的存在。

这是WeakHashMap提供的:

基于哈希表的Map接口实现,带有 弱键
。当a中的某个条目WeakHashMap不再用作其键时,该条目将自动删除。更准确地说,给定键的映射的存在不会阻止该键被垃圾收集器丢弃,即被终结化,终结化和回收。丢弃键后,其条目会从映射中有效删除,因此此类的行为与其他Map实现有所不同。

条目的删除不是瞬时的。它依赖于将排队WeakReference到a中ReferenceQueue,然后在进行下一个查询(例如containsValue或)时在内部对其进行轮询size()。例如,如果我将您的程序更改为:

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此时已证明关键实例已被垃圾回收,但它偶尔还是打印“ Value present”
。如上所述,这与地图的内部清理有关,而不是与PersonMetadata我们始终保持强烈引用的实例有关meta

使有PersonMetadata资格进行垃圾回收是完全不同的事情。如前所述,WeakReference每当我们调用方法an时,都会进行内部清理。如果我们不这样做,就不会进行清理,因此即使密钥已被垃圾回收,它仍然是一个强大的参考。考虑:

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;语句时,该映射在最后(内部清除之后)仍将为空,但不会收集该值。

重要的是要记住,这些代码示例仅用于演示目的并涉及特定于实现的行为,最值得注意的是,main方法通常未优化运行。正式地,如果未使用引用对象,则不需要局部变量来防止垃圾收集,这在优化方法时实际上具有相关性。



 类似资料:
  • 问题内容: 我正在使用实现缓存。我想知道是否要遍历此映射的键,同时垃圾回收器正在主动从此映射中删除键,我会收到吗?我不这么认为,因为据我所知,并发修改异常是由于应用程序代码中的错误而发生的,开发人员忘记了同一个映射是由其他线程共享/使用的,因此在这种情况下,应该不会发生。但是想知道当WeakHashMap不同步时JVM将如何处理吗? 问题答案: 正如bkail所说,当GC从a中“删除”一个条目时,

  • 垃圾回收 我们对生产中花了很多时间来调整垃圾回收。垃圾回收的关注点与Java大致相似,尽管一些惯用的Scala代码比起惯用的Java代码会容易产生更多(短暂的)垃圾——函数式风格的副产品。Hotspot的分代垃圾收集通常使这不成问题,因为短暂的(short-lived)垃圾在大多情形下会被有效的释放掉。 在谈GC调优话题前,先看看这个Attila的报告,它阐述了我们在GC方面的一些经验。 Scal

  • 对于开发者来说,JavaScript 的内存管理是自动的、无形的。我们创建的原始值、对象、函数……这一切都会占用内存。 当我们不再需要某个东西时会发生什么?JavaScript 引擎如何发现它并清理它? 可达性(Reachability) JavaScript 中主要的内存管理概念是 可达性。 简而言之,“可达”值是那些以某种方式可访问或可用的值。它们一定是存储在内存中的。 这里列出固有的可达值的

  • 垃圾收集,引用计数,显式分配 和所有的现代语言一样,OCaml提供垃圾收集器,所以你不用像C/C++一样显式地分配和释放内存。 JWZ在他的文章 "Java sucks" rant(Java蛋疼(怒)!): 第一个好家伙是Java没有 free()。其他的都没有所谓了。这几乎掩盖了所有的缺点,不管有多糟糕, 这个有点让后续文档基本都没有意义了,但是...(译注:但是啥大家自己看吧) OCaml的垃

  • 问题内容: 我要求显式垃圾回收器调用。但是窗口并没有从屏幕上消失,为什么垃圾回收器不回收JFrame的对象? 问题答案: 当被创建时,它注册本身在一些内部摇摆的数据结构,其允许它接收到类似的鼠标点击的事件。这意味着在您告诉Swing使用摆脱窗口之前,存在对潜伏在某处的对象的引用。

  • 主要内容:垃圾回收器函数,实例Lua 采用了自动内存管理。 这意味着你不用操心新创建的对象需要的内存如何分配出来, 也不用考虑在对象不再被使用后怎样释放它们所占用的内存。 Lua 运行了一个垃圾收集器来收集所有死对象 (即在 Lua 中不可能再访问到的对象)来完成自动内存管理的工作。 Lua 中所有用到的内存,如:字符串、表、用户数据、函数、线程、 内部结构等,都服从自动管理。 Lua 实现了一个增量标记-扫描收集器。 它使用