WeakHashMap的使用

WeakHashMap的使用

WeakHashMap的使用与HashMap类似,但是区别在于WeakHashMap对Key保留了弱引用(弱引用,四大引用类型中的一种。对于只有弱引用的对象而言,当垃圾回收进行时,无论系统内存是否足够,总会回收该对象所占用的内存),而HashMap保留了对Key的强引用。也就是说只要该HashMap存在,那么Key所引用的对象就不会被回收。而WeakHashMap的Key没有其他强引用时,这些key就可能被回收,且WeakHashMap可能会回收这些Key对应的键值对。

这里再次强调一下:WeakHashMap Weak在与Key,而非Value!

代码验证

我们通过代码来验证一下WeakHashMap的这个特性。

public class Main {
    public static void main(String[] args) {
        WeakHashMap<String, String> map = new WeakHashMap<>();
        String value = new String("value");
        map.put(new String("key"), value); // {key=value}
        System.out.println(map);

        System.gc();
        System.runFinalization();

        System.out.println(map);     // {}
    }
}

首先,value对象持有了对字符串对象“value”的强引用,然后将“key-value”这个字符串键值对放入map。注意这里有个小细节,就是key使用了new String() 生成,而非直接使用key的字符串常量。为什么呢?这是因为对于可在编译阶段确定的字符串,系统的字符串常量池会直接记录它,自动保留对它的强引用。此情况下,GC无法从WeakHashMap中删除这个Key。而使用new String生成的字符串对象无法在编译阶段中确定,需要在运行时生成此字符串。而且其存储位置是堆,而非字符串常量池。这样,我们通过new String()这种方式避免了对字符串“key”施加强引用。经过GC后,根据代码的输出结果,我们可以很明显的看到这个“key”被回收了,其对应的键值对也被删除。

对于下面这个情况:

public class Main {
    public static void main(String[] args) {
        WeakHashMap<String, String> map = new WeakHashMap<>();
        String value = new String("value");
        map.put("key", value);
        System.out.println(map); // {key=value}

        System.gc();
        System.runFinalization();

        System.out.println(map); // {key=value}
    }
}

由于直接使用了字符串字面量“key”,造成了系统对“key”字符串的缓存,对其施加了强引用,因此GC未能销毁此实例。

再看下面这个情况,读者能看出为什么GC没有起作用吗?

class TestPool<T> {
    private WeakHashMap<T, T> map = new WeakHashMap<>();

    public T intern(T var) {
        if (map.get(var) != null) {
            return map.get(var);
        }
        map.put(var, var);
        return var;
    }

    public void printMap(){
        System.out.println(map);
    }
}

public class Main {
    private static void gc() {
        System.gc();
        System.runFinalization();
    }

    public static void main(String[] args) {
        TestPool<String> testPool = new TestPool<>();
        testPool.intern(new String("testPool"));
        testPool.printMap();    //{testPool=testPool}
        gc();
        testPool.printMap();    //{testPool=testPool}
    }
}

这其实是笔者模拟实现String.intern()方法的一个失败的例子。这里将同一个字符串变量即作为了键,也作为了值。作为键,它就拥有了弱引用。而同时它又是这个键值对的值,不过WeakHashMap对值是正常的强引用。因此这个键值对字符串testPool同时用了弱引用和强引用,而GC对拥有强引用的对象是不处理的,因此GC对这个WeakHashMap中特殊的键值对是束手无策的。

下面这个就可以,读者可以好好对比一下:

class TestPool_KV {
    private WeakHashMap<String, MyObject> map = new WeakHashMap<>();

    public MyObject intern(String key, MyObject value) {
        MyObject object = map.get(key);
        if (object != null) {
            return object;
        }
        map.put(key, value);
        return value;
    }

    public void printMap() {
        System.out.println(map);
    }
}

class MyObject {

}

public class Main {
    private static void gc() {
        System.gc();
        System.runFinalization();
    }

    public static void main(String[] args) {
        TestPool_KV pool_KV = new TestPool_KV();
        pool_KV.intern(new String("name"), new MyObject());
        pool_KV.printMap();     //{name=MyObject@15db9742}
        gc();
        pool_KV.printMap();     //{}
    }
}

总结

综上所述,WeakHashMap是WeakReference在键上,而非值上。这一点我们要严加注意。

  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值