侯体宗的博客
  • 首页
  • Hyperf版
  • beego仿版
  • 人生(杂谈)
  • 技术
  • 关于我
  • 更多分类
    • 文件下载
    • 文字修仙
    • 中国象棋ai
    • 群聊
    • 九宫格抽奖
    • 拼图
    • 消消乐
    • 相册

Java的WeakReference与WeakHashMap

Java  /  管理员 发布于 5年前   427

首先看看 WeakReference

wiki 上 Weak reference 的一个例子:

public class ReferenceTest {public static void main(String[] args) throws InterruptedException {             WeakReference r = new WeakReference(new String("I'm here"));            WeakReference sr = new WeakReference("I'm here");            System.out.println("before gc: r=" + r.get() + ", static=" + sr.get());            System.gc();            Thread.sleep(100);             // only r.get() becomes null            System.out.println("after gc: r=" + r.get() + ", static=" + sr.get()); }}



输出:
before gc: r=I'm here, static=I'm here
after gc: r=null, static=I'm here

为什么“only r.get() becomes null”?

最容易想到的原因是 string-pool
1.new String("I'm here") 是在堆上创建了一个 String 对象,由于程序中没有对这个对象的强引用,因此会被回收掉
如果是下面这样则不会回收:
String str = new String("I'm here");
WeakReference r = new WeakReference(str);
2.而 直接量"I'm here" 是从 string-pool 中取得的,它被 string-pool 强引用了,因此不会被回收

但 stackoverflow 上有人说本质上不是这个原因(链接:http://stackoverflow.com/questions/14494875/weakreference-string-didnt-garbage-collected-how):

It is NOT because of string pooling per se.The real reason is that the ReferenceTest class has an implicit hard reference to the String object that represents the "I'm here"literal. That hard reference means that the weak reference in sr won't be broken by the garbage collection.



是因为 ReferenceTest 对 直接量 "I'm here" 有强引用?没看懂。。

看完了弱引用,再看看 Java 中其他的几种引用类型

1.强引用
这个没啥好说,通常情况下的引用是强引用

2.软引用
与弱引用很相似,但生命周期比弱引用要长。在内存不是那么紧张时,不会被回收

3.弱引用
主要是方便和加快垃圾回收,避免对象长驻内存
如果一个对象的所有引用都是弱引用(称为 weakly reachable)的话,那一旦被垃圾回收器注意到,就会被回收
wiki 上指出弱引用的应用场合:
a.在“引用计数法”的垃圾回收机制中,能避免“循环引用”,因为 Weak references are not counted in reference counting
b."key-value"形式的数据结构中,key 可以是弱引用。例如 Map
c.观察者模式(特别是事件处理)中,观察者或者 listener 如果采用弱引用,则不需要显式的移除
d.缓存

4.虚引用
虚引用对对象的引用作用很弱,你无法通过虚引用来访问一个对象:虚引用的 get 方法总是返回 null
虚引用主要用来跟踪对象被垃圾回收的活动
它跟弱引用的区别是,一旦对象是  weakly reachable,弱引用就会被加入到 Reference queues 当中,这发生在 finalize 和 GC 之前;
而虚引用,只有在对象真正被清除出内存时,才会被加入
它有两大作用:
a.通过虚引用可以知道一个对象何时被清除出内存。事实上,这也是唯一的途径
b.防止对象在 finalize 方法中被重新“救活”(可参考《深入理解 Java 虚拟机》一书)

接下来我们看看 Java 的 WeakHashMap,它的 key 是弱引用,在 key 不再需要的时候,就可以把 key-value(下面统称为entry) 从 WeakHashMap 当中移除,并最终被回收

什么情况下需要用到  WeakHashMap?

例如:

public class SocketManager {    private Map<Socket,User> m = new HashMap<Socket,User>();     public void setUser(Socket s, User u) {        m.put(s, u);    }    public User getUser(Socket s) {        return m.get(s);    }    public void removeUser(Socket s) {        m.remove(s);    }} SocketManager socketManager;...socketManager.setUser(socket, user);



上述程序中, 我们把 socket 称为 transient objects,而 User 称为 metadata
问题是:
当 socket 不再需要(例如关闭了)时,它和它关联的 User 仍在 Map 中,这就造成了内在泄漏(the lifetime of the Socket-User mapping should be matched to the lifetime of the Socket, but the language does not provide any easy means to enforce this rule)

解决办法就是使用 WeakHashMap:

public class SocketManager {    private Map<Socket,User> m = new WeakHashMap<Socket,User>();     public void setUser(Socket s, User u) {        m.put(s, u);    }    public User getUser(Socket s) {        return m.get(s);    }}



WeakHashMap 中, value 是强引用。这会带来一个问题,当 key 被回收时,value 仍存活
因此,要加上一个类似 listener 的功能,当 key 回收时,把 WeakReference 放入  Reference queues,执行相关的清理操作
参见 WeakReference 的构造函数:WeakReference(T referent, ReferenceQueue<? super T> q)

那 entry 的删除是什么时候执行?
看看 WeakHashMap 的源码:

private static class Entry<K,V> extends WeakReference<K> implements Map.Entry<K,V> {        private V value;        private final int hash;        private Entry<K,V> next;        Entry(K key, V value,      ReferenceQueue<K> queue,              int hash, Entry<K,V> next) {            super(key, queue);//可以看到,entry 的 key 是被 WeakReference 封装起来的            this.value = value;            this.hash  = hash;            this.next  = next;        }//...}public int size() {if (size == 0)return 0;expungeStaleEntries();return size;}public V put(K key, V value) {K k = (K) maskNull(key);int h = HashMap.hash(k.hashCode());Entry[] tab = getTable();//...} private Entry[] getTable() {expungeStaleEntries();return table;}/*这个方法就是把处于  Reference queues 中的 entry 从 WeakHashMap 中移除其实是很简单的链表操作,例如a -> b -> c删除 b ,就让 a 直接指向 c:a -> c注意到这只是把 b 从链表中删除并且使 b 指向 null,但此时 GC 仍未发生,对象可能仍在内存中那什么时候把 entry 加入 Reference queues 呢?是在 WeakReference 的父类 Reference 做的*/private void expungeStaleEntries() {Entry<K,V> e;while ( (e = (Entry<K,V>) queue.poll()) != null) {int h = e.hash;int i = indexFor(h, table.length);Entry<K,V> prev = table[i];Entry<K,V> p = prev;while (p != null) {Entry<K,V> next = p.next;if (p == e) {if (prev == e)table[i] = next;elseprev.next = next;e.next = null;  // Help GCe.value = null; //  "   "size--;break;}prev = p;p = next;}}}


注意到 expungeStaleEntries 这个方法被 size() 和 getTable() 调用,而 getTable() 又被 get/put/resize/remove 等方法调用
因此, WeakHashMap 并不是自动的删除  entry,只有当你调用了上述方法时,才会删除 entry

这通常不会引起什么问题,WeakHashMap 创建后就不再执行 size/get/put/resize/remove 这些方法的情况并不多


参考文章:
http://en.wikipedia.org/wiki/Weak_reference
https://www.ibm.com/developerworks/java/library/j-jtp11225/
https://weblogs.java.net/blog/2006/05/04/understanding-weak-references
http://www.iteye.com/topic/587995
http://www.jb51.net/article/36948.htm



  • 上一条:
    java web开发 高并发处理
    下一条:
    Java常用工具包 Jodd
  • 昵称:

    邮箱:

    1条评论 (评论内容有缓存机制,请悉知!)
    最新最热
    • 分类目录
    • 人生(杂谈)
    • 技术
    • linux
    • Java
    • php
    • 框架(架构)
    • 前端
    • ThinkPHP
    • 数据库
    • 微信(小程序)
    • Laravel
    • Redis
    • Docker
    • Go
    • swoole
    • Windows
    • Python
    • 苹果(mac/ios)
    • 相关文章
    • 在java中实现的脱敏工具类代码示例分享(0个评论)
    • zookeeper安装流程步骤(0个评论)
    • 在java中你背的“八股文”可能已经过时了(2个评论)
    • 在php8.0+版本中使用属性来增加值代码示例(3个评论)
    • java 正则表达式基础,实例学习资料收集大全 原创(0个评论)
    • 近期文章
    • 在go语言中使用api.geonames.org接口实现根据国际邮政编码获取地址信息功能(1个评论)
    • 在go语言中使用github.com/signintech/gopdf实现生成pdf分页文件功能(0个评论)
    • gmail发邮件报错:534 5.7.9 Application-specific password required...解决方案(0个评论)
    • 欧盟关于强迫劳动的规定的官方举报渠道及官方举报网站(0个评论)
    • 在go语言中使用github.com/signintech/gopdf实现生成pdf文件功能(0个评论)
    • Laravel从Accel获得5700万美元A轮融资(0个评论)
    • 在go + gin中gorm实现指定搜索/区间搜索分页列表功能接口实例(0个评论)
    • 在go语言中实现IP/CIDR的ip和netmask互转及IP段形式互转及ip是否存在IP/CIDR(0个评论)
    • PHP 8.4 Alpha 1现已发布!(0个评论)
    • Laravel 11.15版本发布 - Eloquent Builder中添加的泛型(0个评论)
    • 近期评论
    • 122 在

      学历:一种延缓就业设计,生活需求下的权衡之选中评论 工作几年后,报名考研了,到现在还没认真学习备考,迷茫中。作为一名北漂互联网打工人..
    • 123 在

      Clash for Windows作者删库跑路了,github已404中评论 按理说只要你在国内,所有的流量进出都在监控范围内,不管你怎么隐藏也没用,想搞你分..
    • 原梓番博客 在

      在Laravel框架中使用模型Model分表最简单的方法中评论 好久好久都没看友情链接申请了,今天刚看,已经添加。..
    • 博主 在

      佛跳墙vpn软件不会用?上不了网?佛跳墙vpn常见问题以及解决办法中评论 @1111老铁这个不行了,可以看看近期评论的其他文章..
    • 1111 在

      佛跳墙vpn软件不会用?上不了网?佛跳墙vpn常见问题以及解决办法中评论 网站不能打开,博主百忙中能否发个APP下载链接,佛跳墙或极光..
    • 2016-11
    • 2018-03
    • 2020-03
    • 2023-05
    • 2023-11
    • 2024-01
    Top

    Copyright·© 2019 侯体宗版权所有· 粤ICP备20027696号 PHP交流群

    侯体宗的博客