Java WeakHashMap何时清除null键?

问题描述 投票:2回答:4

在下面的代码中nameRef.get()name = nullSystem.gc()之后为null。

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
    }    
}

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 ?
    }    
}

Java WeakHashMap何时清理null密钥?

java garbage-collection weak-references weakhashmap
4个回答
7
投票

简单的答案:你不知道。

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

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

是的,另一部分是调用System.gc()只是建议jvm进行垃圾收集。在标准设置中,如果jvm遵循该请求,则无控制,或忽略它。您需要使用非常特殊的JVM来更改它。

通常,gc只在jvm认为必要时才会运行。因此,只要内存不足,您的地图就可以将其大小保持为1。正如Holger的answer很好地概述了GC运行并不会强制地图更新那部分。


3
投票

System.gc()只建议垃圾收集。没有任何保证何时,即使收集将发生。你看到了这个,你提出了建议,但还没有完成。

documentation

[...]调用gc方法表明Java虚拟机花费了很多精力来回收未使用的对象,以便使它们当前占用的内存可用于快速重用。当控制从方法调用返回时,Java虚拟机已尽最大努力从所有丢弃的对象中回收空间。 [...]

话虽如此,除了定期检查之外,您无法知道地图中的条目何时消失。


3
投票

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

但是在你的具体设置中,似乎System.gc()足以收集物体,正如你的WeakReference例子所确定的那样。但清除弱引用与排队引用不同,允许WeakHashMap执行清理。正如在this answer中所解释的那样,WeakHashMap的清理依赖于要入队的关键引用,每次你在它上面调用一个方法时都会检查它,这是你的例子中的后续map.size()调用。

正如the documentation of 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
    }    
}

当然,这并没有改变这个测试很简单但不保证行为的事实,所以你不应该依赖它来编写生产代码。


0
投票

你的演示有一些问题。实际上,System.gc()之后的大小为0。在你的情况是因为当打印地图的大小GC还没有完成,所以结果是1

© www.soinside.com 2019 - 2024. All rights reserved.