关于WeakHashMap的垃圾回收的问题

WeakHashMap 继承于AbstractMap,实现了Map接口。
    和HashMap一样,WeakHashMap 也是一个散列表,它存储的内容也是键值对(key-value)映射,而且键和值都可以是null
   不过WeakHashMap的键是“弱键”。在 WeakHashMap 中,当某个键不再正常使用时,会被从WeakHashMap中被自动移除。更精确地说,对于一个给定的键,其映射的存在并不阻止垃圾回收器对该键的丢弃,这就使该键成为可终止的,被终止,然后被回收。某个键被终止时,它对应的键值对也就从映射中有效地移除了。
    这个“弱键”的原理呢?大致上就是,通过WeakReference和ReferenceQueue实现的。 WeakHashMap的key是“弱键”,即是WeakReference类型的;ReferenceQueue是一个队列,它会保存被GC回收的“弱键”。实现步骤是:
    (01) 新建WeakHashMap,将“键值对”添加到WeakHashMap中。
           实际上,WeakHashMap是通过数组table保存Entry(键值对);每一个Entry实际上是一个单向链表,即Entry是键值对链表。
   (02) 当某“弱键”不再被其它对象引用,并被GC回收时。在GC回收该“弱键”时,这个“弱键”也同时会被添加到ReferenceQueue(queue)队列中。
   (03) 当下一次我们需要操作WeakHashMap时,会先同步table和queue。table中保存了全部的键值对,而queue中保存被GC回收的键值对;同步它们,就是删除table中被GC回收的键值对
   这就是“弱键”如何被自动从WeakHashMap中删除的步骤了。

和HashMap一样,WeakHashMap是不同步的。可以使用 Collections.synchronizedMap 方法来构造同步的 WeakHashMap

以上为WeakHashMap的基本介绍,不过多叙说,这里说一个问题,当key置位null的时候,需要在gc进行一次垃圾回收之后,再回把对应的key,value都回收掉,这里回收有一个时差,会出现明明这个key和map已经不存在于map中了,但是通过mao.size()获取大小的时候,依旧会统计上。

import java.util.HashMap;
import java.util.WeakHashMap;
public class WeakReferenceDemo {
    public static void main(String[] args) {
        weakMap();
    }
    private static void weakMap() {
        WeakHashMap hashMap = new WeakHashMap<>();
        String value = "WeakHashMao";
        Integer key = new Integer(1);
        hashMap.put(key, value);
        System.out.println(hashMap);
        key = null; //将key置位null
        System.out.println(hashMap);
        System.gc(); //这里模拟一次GC过程
        System.out.println(hashMap + "\t " + hashMap.size());
    }
}

运行结果预计的是

{1=WeakHashMao}
{1=WeakHashMao}
{}	 0

但是实际的确实:

//结果一
{1=WeakHashMao}
{1=WeakHashMao}
{}	 1
//结果二
{1=WeakHashMao}
{1=WeakHashMao}
{}	 0

交替出现。

分析出现的原因,可能是gc执行过程。加上 -XX:+PrintGCDetails 看一下gc的日志, 

{1=WeakHashMao}
{1=WeakHashMao}
[GC (System.gc()) [PSYoungGen: 2663K->800K(38400K)] 2663K->808K(125952K), 0.0052766 secs] [Times: user=0.01 sys=0.00, real=0.00 secs] 
[Full GC (System.gc()) [PSYoungGen: 800K->0K(38400K)] [ParOldGen: 8K->642K(87552K)] 808K->642K(125952K), [Metaspace: 3150K->3150K(1056768K)], 0.0135799 secs] [Times: user=0.00 sys=0.00, real=0.02 secs] 
{}	 1
{1=WeakHashMao}
{1=WeakHashMao}
[GC (System.gc()) [PSYoungGen: 2663K->704K(38400K)] 2663K->712K(125952K), 0.0014530 secs] [Times: user=0.00 sys=0.00, real=0.00 secs] 
[Full GC (System.gc()) [PSYoungGen: 704K->0K(38400K)] [ParOldGen: 8K->617K(87552K)] 712K->617K(125952K), [Metaspace: 3130K->3130K(1056768K)], 0.0054741 secs] [Times: user=0.01 sys=0.00, real=0.01 secs] 
{}	 0

通过两次gc看出来,第一次新生代的额gc,堆区的内存从2663k - > 704k,第二次gc,清理新生代的垃圾 704 全部清空了,老年代的内存总内存是 8K + 704K = 712K, 最后变为 617K 。

从这里可以看出来,在堆内存new的对象,在经历 full gc的时候,堆内存的对象,进入了老年代。

从垃圾回收这边没看出问题。

 

那么接下来看看线程执行情况

关于WeakHashMap的垃圾回收的问题_第1张图片

main线程的已经执行完毕了,但是调用的map.size方法还在执行,由此可以得出以下几个原因:

1,异步调用,但是代码中没有异步的指令。 所以排除

2,指令重排序,重排的地方是这里

关于WeakHashMap的垃圾回收的问题_第2张图片

先执行了return size; 

然后在执行

expungeStaleEntries();

个人见解,如有其他意见,请指点。

 

 

参考文档:

https://blog.csdn.net/qiuhao9527/article/details/80775524

你可能感兴趣的:(关于WeakHashMap的垃圾回收的问题)