`

Java的WeakReference与WeakHashMap

阅读更多
首先看看 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;
				else
					prev.next = next;
				e.next = null;  // Help GC
				e.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
0
0
分享到:
评论

相关推荐

    Java编程WeakHashMap实例解析

    Java编程WeakHashMap实例解析 WeakHashMap是Java编程中的一种特殊的HashMap实现,它使用弱引用来保存键和值,这样可以使得垃圾回收器自动清理键和值。在WeakHashMap中,键和值都是弱引用的,这样可以避免内存泄露...

    WeakHashMap的使用方法详解

    WeakHashMap是Java中的一种哈希映射表,它的键是弱引用的,意味着当 WeakHashMap 的键对象只有 WeakHashMap 自己持有时,垃圾回收器可以将其回收。WeakHashMap 的使用方法主要体现在以下几个方面: 1. WeakHashMap...

    Java引用类型1

    WeakReference&lt;Widget&gt; weakWidget = new WeakReference(widget); ``` - 使用`WeakHashMap`可以自动管理弱引用键,当键对象被回收时,对应的条目也会自动删除。 4. **虚引用(Phantom Reference)** - 虚引用是...

    java 引用相关文档

    在Java编程语言中,引用是连接对象实例与内存空间的关键概念。这个“java 引用相关文档”可能包含了深入探讨Java引用的各种类型及其特性的内容。在这里,我们将详细讲解Java中的四种引用类型:强引用、软引用、弱...

    10分钟带你理解Java中的弱引用

    在Java中,弱引用由`java.lang.ref.WeakReference&lt;T&gt;`类表示。官方文档指出,弱引用的对象不会阻止它所指向的对象被垃圾回收器回收。即使有弱引用指向一个对象,只要没有其他强引用存在,垃圾收集器就会回收这个对象...

    Java通过What、Why、How了解弱引用

    在Java编程语言中,弱引用是一种特殊的引用类型,它允许...通过WeakReference类以及相关数据结构如WeakHashMap,Java为开发者提供了实现这一目标的工具。在实际应用中,合理使用弱引用能够提升程序的性能和稳定性。

    picketlink-jbas-common-2.6.0.CR4.zip

    【描述】"Java-WeakIdentityHashMap.zip, weakhashmap identityhashmapa独立库的组合,用于weakidentityhashmap实现内/外字段" 描述的是一个特定的Java数据结构实现,即WeakIdentityHashMap。这是一个结合了...

    Java Reference源码解析

    3. 弱引用(WeakReference):弱引用,非强引用和软引用,但是可以通过弱引用对象来访问。弱引用的对象,不管内存是否足够,只要被垃圾收集器发现,该引用的对象就会被回收。实际的应用见WeakHashMap等。 4. 虚引用...

    全面解析Java中的GC与幽灵引用

    本文将深入探讨Java中的四种引用类型:强引用(StrongReference)、软引用(SoftReference)、弱引用(WeakReference)以及幽灵引用(PhantomReference),并讨论它们在GC工作原理和性能调优中的作用。 1. 强引用...

    Java中几个Reference常见的作用详解

    `WeakReference`常用于实现弱集合,如`WeakHashMap`,这样可以避免因集合中的元素导致对象无法被回收。 3. **SoftReference**: 软引用在内存管理策略上介于强引用和弱引用之间。当系统内存不足但尚未达到OOM状态...

    深入理解Java中的弱引用

    Java 提供了 `WeakHashMap` 类,它的键使用弱引用,这样当键所对应的对象不再被其他强引用时,键及其关联的值都会被自动清除,从而避免了内存泄漏。 虚引用(Phantom Reference)是最弱的引用类型,它甚至无法获取...

    Android仿String的对象驻留示例分析

    private final WeakHashMap, WeakReference&lt;T&gt;&gt; pool = new WeakHashMap(); public T get(T object) { T res; WeakReference&lt;T&gt; ref = pool.get(object); if (ref != null) { res = ref.get(); } else { ...

    详解Java对象的强、软、弱和虚引用+ReferenceQueue

    软引用可以和一个引用队列(ReferenceQueue)联合使用,如果软引用所引用的对象被垃圾回收器回收,Java虚拟机就会把这个软引用加入到与之关联的引用队列中。 三、弱引用(WeakReference) 弱引用与软引用的区别...

    WeakObjectPool:用于在对象生命周期中将对象附加到对象(即装饰器)的池

    您是否曾经尝试过在运行时扩展Java对象?解决方案WeakObjectPool :用于保存和扩展[弱引用的对象]的池( “ title =” WeakReference JavaDoc) 。 这与有何不同? 这里有几个关键的区别,它们有效地定义了...

    详解java中Reference的实现与相应的执行过程

    在Java中,`Reference`类是一个特殊...在实际编程中,选择合适的引用类型取决于应用场景,例如,使用 `WeakHashMap` 可以在对象不再被强引用时自动移除,而 `PhantomReference` 则用于在对象被彻底回收后执行清理工作。

    Java中弱引用和软引用的区别以及虚引用和强引用介绍

    - **WeakHashMap**:这个特殊的Map实现使用弱引用作为键,当键的强引用消失时,键值对将自动从映射中移除,有助于防止内存泄漏。 - **资源清理**:虚引用配合引用队列可以实现对象回收后的资源清理,例如关闭文件流...

    Tomcat 检测内存泄漏实例详解

    它利用了 Java 中的弱引用(WeakReference)特性,通过 WeakHashMap 来跟踪 WebappClassLoader。弱引用对象只有在没有任何其他强引用指向它时,才会在下一次垃圾回收时被清除。Tomcat 将 WebappClassLoader 作为键放...

    AndroidApp定位和规避内存泄露方法研究

    在Android系统中,由于Java的自动垃圾回收机制,开发者通常不会像C++那样手动管理内存。然而,由于引用计数或不恰当的对象生命周期管理,仍然可能出现内存泄露。 在Android中,常见的内存泄露类型包括: 1. 单例...

Global site tag (gtag.js) - Google Analytics