Java WeakHashMap何时会清理空键?

时间:2019-03-02 13:42:42

标签: java garbage-collection weak-references weakhashmap

nameRef.get()name = null之后的System.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键?

4 个答案:

答案 0 :(得分:7)

简单的答案:您不知道。

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

因此,您无法知道该地图的“状态”何时更改。

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

通常,仅当jvm认为有必要时,才会运行gc。因此,只要不占用内存,您的地图就可以保持其大小为1。正如Holger的answer所概述的那样,即使GC运行也不会强制地图更新该部分。

答案 1 :(得分:3)

System.gc()仅建议进行垃圾回收。无法保证收集时间和时间。您确实看到了,您提出了建议,但尚未完成。

请参见documentation

  

[...]调用gc方法建议,Java虚拟机将花费大量精力来回收未使用的对象,以使它们当前占用的内存可用于快速重用。 当控件从方法调用返回时,Java虚拟机已尽力从所有丢弃的对象中回收空间。 [...]

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

答案 2 :(得分:3)

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

但是在您的特定设置中,似乎System.gc()足以收集对象,具体取决于您的WeakReference示例。但是清除弱引用与使引用入队不同,以允许WeakHashMap执行其清理。如this answer中所述,WeakHashMap的清理依赖于要排队的键引用,每次您在该键引用上调用方法时都会对其进行检查,即随后的{{1} }在您的示例中调用。

根据the documentation of WeakReference指定:

  

假设垃圾收集器在某个时间点确定对象是弱可到达的。到那时,它将自动清除对该对象的所有弱引用,以及对所有其他弱可达对象的弱引用,这些对象都可以通过一系列强引用和软引用从该对象到达。同时,它将声明所有以前难以到达的对象都是可终结的。同时或在以后的某个时间,它将把那些新近清除的弱引用加入队列,该弱引用已在引用队列中注册。

请注意,清除操作是在那时 发生的,而入队将在“ 同时或以后的某个时间”发生。

在使用HotSpot JVM / OpenJDK的情况下,垃圾回收后异步进行排队,并且您的主线程运行速度太快,因此关键引用尚未排队。

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

map.size()

当然,这不会改变这样的事实:它可以很好地进行测试,但不能保证行为,因此您不应该依赖它来编写生产代码。

答案 3 :(得分:0)

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