Java的WeakReference与WeakHashMap
Java  /  管理员 发布于 4年前   395
首先看看 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
123 在
Clash for Windows作者删库跑路了,github已404中评论 按理说只要你在国内,所有的流量进出都在监控范围内,不管你怎么隐藏也没用,想搞你分..原梓番博客 在
在Laravel框架中使用模型Model分表最简单的方法中评论 好久好久都没看友情链接申请了,今天刚看,已经添加。..博主 在
佛跳墙vpn软件不会用?上不了网?佛跳墙vpn常见问题以及解决办法中评论 @1111老铁这个不行了,可以看看近期评论的其他文章..1111 在
佛跳墙vpn软件不会用?上不了网?佛跳墙vpn常见问题以及解决办法中评论 网站不能打开,博主百忙中能否发个APP下载链接,佛跳墙或极光..路人 在
php中使用hyperf框架调用讯飞星火大模型实现国内版chatgpt功能示例中评论 教程很详细,如果加个前端chatgpt对话页面就完美了..Copyright·© 2019 侯体宗版权所有· 粤ICP备20027696号