Java WeakHashMap何时清除null键?

let*_*ian 2 java garbage-collection weak-references weakhashmap

在下面的代码nameRef.get()是null,之后name = nullSystem.gc().

import java.lang.ref.WeakReference;

public class Main {

    public static void main(String[] args) {
        String name = new String("ltt");

        WeakReference<String> nameRef = new WeakReference<>(name);    
        System.out.println(nameRef.get()); // ltt

        name = null;
        System.gc();

        System.out.println(nameRef.get());  // null
    }    
}
Run Code Online (Sandbox Code Playgroud)

WeakHashMap基于WeakReference.最后,我认为map.size()将是0.事实上,它是1.

import java.util.WeakHashMap;

public class Main2 {

    public static void main(String[] args) {
        String name = new String("ltt");
        WeakHashMap<String, Integer> map = new WeakHashMap<>();
        map.put(name, 18);
        System.out.println(map.size()); // 1

        name = null;
        System.gc();

        System.out.println(map.size());  // it's 1. why not 0 ?
    }    
}
Run Code Online (Sandbox Code Playgroud)

Java WeakHashMap何时清理null密钥?

Gho*_*ica 7

简单的答案:你不知道.

含义:当jvm启动并触发GC循环时,您无法控制.实际收集符合条件的对象时,您无法控制.

因此,您无法知道该地图的"状态"何时发生变化.

是的,另一部分是调用System.gc()只是建议jvm进行垃圾收集.同样,如果jvm遵循该请求,您将无法控制,或忽略它.

通常,gc只在jvm认为必要时才会运行.因此,只要内存不足,您的地图就可以将其大小保持为1.


Hol*_*ger 6

正如其他人指出的那样,无法保证System.gc()执行实际的垃圾收集。垃圾收集周期也不能保证实际收集特定的不可达对象。

但是在您的特定设置中,System.gc()根据您的WeakReference示例确定,这似乎足以收集对象。但是清除弱引用与将引用排入队列不同,以允许WeakHashMap执行其清理。正如在这个答案中所解释的,清理WeakHashMap依赖于要排队的键引用,每次调用它的方法时都会检查它,这是map.size()您的示例中的后续调用。

作为文档WeakReference指定:

假设垃圾收集器在某个时间点确定某个对象是弱可达的。那时它将原子地清除对该对象的所有弱引用以及对任何其他弱可达对象的所有弱引用,通过强引用和软引用链可以从中访问该对象。同时它将声明所有以前弱可达的对象是可终结的。同时或稍后它会将那些新清除的已注册到引用队列的弱引用加入队列。

请注意清除是如何“在那个时间”发生的,而入队将“同时或稍后”发生。

在 HotSpot JVM/OpenJDK 的情况下,在垃圾收集之后排队发生异步,并且您的主线程似乎运行得太快,因此键引用尚未入队。

插入一个小的暂停可能会导致示例程序在典型环境中成功:

import java.util.WeakHashMap;

public class Main2 {

    public static void main(String[] args) throws InterruptedException{
        String name = new String("ltt");
        WeakHashMap<String, Integer> map = new WeakHashMap<>();
        map.put(name, 18);
        System.out.println(map.size()); // 1

        name = null;
        System.gc();

        Thread.sleep(10);

        System.out.println(map.size()); // 0
    }    
}
Run Code Online (Sandbox Code Playgroud)

当然,这不会改变这样一个事实,即这对于测试来说是整洁的,但不能保证行为,所以你不应该依赖它来编写生产代码。