Java中WeakHashMap的回收问题详解

 更新时间:2023年09月07日 08:46:14   作者:幸平xp  
这篇文章主要介绍了Java中WeakHashMap的回收问题详解,WeakHashMap弱键大致上是通过WeakReference和ReferenceQueue实现,WeakHashMap的key是"弱键",即是WeakReference类型的,ReferenceQueue是一个队列,它会保存被GC回收的"弱键",需要的朋友可以参考下

一、四大引用

在介绍WeakHashMap回收问题之前,我们要先介绍四大引用类型:

  • 强引用是最传统的“引用”的定义,是指在程序代码之中普遍存在的引用赋值,即类似“Object obj=new Object()”这种引用关系。无论任何情况下,只要强引用关系还存在,垃圾收集器就永远不会回 收掉被引用的对象。
  • 软引用是用来描述一些还有用,但非必须的对象。只被软引用关联着的对象,在系统将要发生内存溢出异常前,会把这些对象列进回收范围之中进行第二次回收,如果这次回收还没有足够的内存, 才会抛出内存溢出异常。在JDK 1.2版之后提供了SoftReference类来实现软引用。
  • 弱引用也是用来描述那些非必须对象,但是它的强度比软引用更弱一些,被弱引用关联的对象只 能生存到下一次垃圾收集发生为止。当垃圾收集器开始工作,无论当前内存是否足够,都会回收掉只 被弱引用关联的对象。在JDK 1.2版之后提供了WeakReference类来实现弱引用。
  • 虚引用也称为“幽灵引用”或者“幻影引用”,它是最弱的一种引用关系。一个对象是否有虚引用的 存在,完全不会对其生存时间构成影响,也无法通过虚引用来取得一个对象实例。为一个对象设置虚 引用关联的唯一目的只是为了能在这个对象被收集器回收时收到一个系统通知。在JDK 1.2版之后提供 了PhantomReference类来实现虚引用。

对于本文主要涉及的为强引用,弱引用。其他引用本篇就不再多提了。 总结一下强引用与弱引用:

  • 强引用:主要为new出来的对象,就算内存满了,也不会回收它。因此当强引用过多时会导致OOM(内存溢出),HashMap中key、value关系就为强引用。
  • 弱引用:被弱引用关联的对象只 能生存到下一次垃圾收集发生为止,而WeakHashMap中的key,value就是弱引用关系。

二、WeakHashMap的回收问题

首先基本大概讲述一下WeakHashMap弱键的原理:

大致上是通过WeakReference和ReferenceQueue实现的。WeakHashMap的key是“弱键”,即是WeakReference类型的;ReferenceQueue是一个队列,它会保存被GC回收的“弱键”。

实现步骤是:

1、新建WeakHashMap,将“键值对”添加到WeakHashMap中。实际上,WeakHashMap是通过数组table保存Entry(键值对);每一个Entry实际上是一个单向链表,即Entry是键值对链表。

2、当某“弱键”不再被其它对象引用,并被GC回收时。在GC回收该“弱键”时,这个“弱键”也同时会被添加到ReferenceQueue(queue)队列中。

3、 当下一次我们需要操作WeakHashMap时,会先同步table和queue。table中保存了全部的键值对,而queue中保存被GC回收的键值对;同步它们,就是删除table中被GC回收的键值对。这就是“弱键”如何被自动从WeakHashMap中删除的步骤了。和HashMap一样,WeakHashMap是不同步的。可以使用 Collections.synchronizedMap 方法来构造同步的 WeakHashMap。

读者看到这可能还是比较抽象。没关系,接下来我们直接上一些例子:

public static void main(String[] args) {
        WeakHashMap<String,String> weakMap = new WeakHashMap<>();
        weakMap.put("k1","v1");
        String k2 = new String("k2");
        weakMap.put(k2,"v2");
        weakMap.put(new String("k3"),"k3");
        System.out.println("weakMap回收前打印结果:");
        System.out.println(weakMap);
        // 因为WeakHashMap内部维护的是一个弱引用映射所以gc直接回收
        System.gc();
        System.out.println("weakMap回收后打印结果:");
        System.out.println(weakMap);
    }

以上的打印结果是什么呢?

在这里插入图片描述

可以看到打印结果是回收后的结果是只有k3的key-value被回收掉了。那么为什么呢?

这得首先从创建一个String对象说起,创建一个String对象最常见的有两种情况:

// 第一种情况
String str1 = "a";
// 第二种情况
String str2 = new String("b");

对于第一种情况其对象的创建会先在字符串常量池判断有没有对应的字符串常量,如果没有创建一个字符串常量一,然后将其引用指向在字符串常量池中的那个常量,如果存在则不需创建直接返回引用。 对于第二种情况其对象的引用还是会先在字符串常量池判断有没有对应的字符串常量,如果没有创建一个字符串常量。 而后在堆中拷贝一个其字符串常量的对象,最后返回堆上的引用

我画了张图对于情况二来讲应该是这个:

在这里插入图片描述

那么对于对象创建的引用我们了解了,那么接下来回到例子就很好理解。

  • 首先是 weakMap.put(“k1”,“v1”); 很明显这里面是k1的引用是放在字符串常量池里的,是一种强引用。因此关于k1的key会一直保持下去。所以就是强引用,因此不会被gc。

然后则是:

String k2 = new String("k2");
weakMap.put(k2,"v2");

这里面的很明显k2为new String的WeakHashMap外部的强引用因此k2的指针其实是在WeakHashMap外部被引用,所以不会被gc。如果要回收它也很容易,则只需要将其外部的引用k2置为null

String k2 = new String("k2");
weakMap.put(k2,"v2");
k2 = null;

而相对直接在WeakHashMap内直接创建对象返回引用的话,那么很明显就是弱键了。

weakMap.put(new String("k3"),"k3");

那么如何可以如何才能不回收这个字符串对象呢?我们刚刚画图提到了,我们可以用intern()方法返回其字符串常量池对应的对象

weakMap.put(new String("k3").intern(),"k3");

这个时候小伙伴可以自己去试一下,此时这个对象gc后也不会被回收了。就如同情况1:weakMap.put(“k1”,“v1”);

最后的最后附上完整代码,有兴趣的可以自己去试试:

public class MapGC {
    public static void main(String[] args) {
        WeakHashMap<String,String> weakMap = new WeakHashMap<>();
        weakMap.put("k1","v1");
        String k2 = new String("k2");
        weakMap.put(k2,"v2");
        weakMap.put(new String("k3"),"k3");
        System.out.println("weakMap回收前打印结果:");
        System.out.println(weakMap);
        // 因为WeakHashMap内部维护的是一个弱引用映射所以gc直接回收
        System.gc();
        System.out.println("weakMap第一次回收后打印结果:");
        System.out.println(weakMap);
        k2 = null;
        weakMap.put(new String("k3").intern(),"k3");
        System.gc();
        System.out.println("weakMap第二次回收后打印结果:");
        System.out.println(weakMap);
    }
}

输出结果:

在这里插入图片描述

到此这篇关于Java中WeakHashMap的回收问题详解的文章就介绍到这了,更多相关WeakHashMap回收内容请搜索脚本之家以前的文章或继续浏览下面的相关文章希望大家以后多多支持脚本之家!

相关文章

  • mybatis分页及模糊查询功能实现

    mybatis分页及模糊查询功能实现

    这篇文章主要为大家详细为大家详细介绍了mybatis实现分页及模糊查询功能,具有一定的参考价值,感兴趣的小伙伴们可以参考一下
    2017-06-06
  • java使用Jsoup连接网站超时的解决方法

    java使用Jsoup连接网站超时的解决方法

    jsoup是一个非常好的解析网页的包,用java开发的,提供了类似DOM,CSS选择器的方式来查找和提取文档中的内容,提取文档内容时会出现超时的情况,解决方法可看下文
    2013-11-11
  • Java应用如何防止恶意文件上传

    Java应用如何防止恶意文件上传

    恶意文件上传可能导致服务器被入侵,数据泄露甚至服务瘫痪,因此我们必须采取全面且有效的防范措施来保护 Java 应用的安全,下面我们就来看看具体的实现方法吧
    2025-05-05
  • Java之Zookeeper注册中心原理剖析

    Java之Zookeeper注册中心原理剖析

    这篇文章主要介绍了Java之Zookeeper注册中心原理剖析,本篇文章通过简要的案例,讲解了该项技术的了解与使用,以下就是详细内容,需要的朋友可以参考下
    2021-07-07
  • PageHelper在springboot+mybatis框架中的使用步骤及原理解析

    PageHelper在springboot+mybatis框架中的使用步骤及原理解析

    这篇文章主要介绍了PageHelper在springboot+mybatis框架中的使用步骤及原理解析,本文通过实例代码给大家介绍的非常详细,对大家的学习或工作具有一定的参考借鉴价值,需要的朋友可以参考下
    2023-03-03
  • SpringBoot项目的多文件兼多线程上传下载

    SpringBoot项目的多文件兼多线程上传下载

    本文主要介绍了SpringBoot项目的多文件兼多线程上传下载,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧
    2023-04-04
  • org.apache.ibatis.annotations不存在的问题

    org.apache.ibatis.annotations不存在的问题

    这篇文章主要介绍了org.apache.ibatis.annotations不存在的问题,具有很好的参考价值,希望对大家有所帮助。如有错误或未考虑完全的地方,望不吝赐教
    2021-10-10
  • 详解maven安装教程以及解决安装不成功的解决办法

    详解maven安装教程以及解决安装不成功的解决办法

    这篇文章主要介绍了详解maven安装教程以及解决安装不成功的解决办法,详细的介绍了几种解决方法,非常具有实用价值,需要的朋友可以参考下
    2018-12-12
  • 关于Integer.parseInt()方法的使用

    关于Integer.parseInt()方法的使用

    这篇文章主要介绍了关于Integer.parseInt()方法的使用,具有很好的参考价值,希望对大家有所帮助。如有错误或未考虑完全的地方,望不吝赐教
    2022-11-11
  • MyBatis 管理和查找TypeHandler的方法

    MyBatis 管理和查找TypeHandler的方法

    MyBatis通过TypeHandlerRegistry组件集中管理和查找TypeHandler,TypeHandler用于处理Java类型和JDBC类型之间的转换,本文介绍MyBatis 管理和查找TypeHandler的方法,感兴趣的朋友一起看看吧
    2025-03-03

最新评论