WeakHashMap源码分析与ThreadLocal的内存泄漏

涉及问题:

  • WeakHashMap和ThreadLocal的异同,两者分别怎样清理失效的Entry?
  • 为什么ThreadLocal会内存泄漏?原理类似的WeakHashMap会内存泄漏吗?

在网上查没看懂,以下是自己看源码后的一些思考,很口语化且不保证一定正确,欢迎纠错

WeakHashMap源码

在这里插入图片描述

WeakHashMap.Entry直接继承自WeakReference,是弱引用本身,它持有的referent指向对象A(key)。另外,WeakHashMap.Entry中持有强引用value

对象A不再被外部引用后会被回收,回收时WeakHashMap.Entry对象作为弱引用加入引用队列queue中

public class WeakHashMap<K,V> extends AbstractMap<K,V> implements Map<K,V> {
   
	/**  
	 * The entries in this hash table extend WeakReference, using its main ref 
	 * field as the key. 
	 */
	private static class Entry<K,V> extends WeakReference<Object> implements Map.Entry<K,V> {
     
	    V value;  
	    final int hash;  
	    Entry<K,V> next;  
	  
	    /**  
	     * Creates new entry.     
	     */    
		Entry(Object key, V value,  
	          ReferenceQueue<Object> queue,  
	          int hash, Entry<K,V> next) {
     
	        super(key, queue);  
	        this.value = value;  
	        this.hash  = hash;  
	        this.next  = next;  
	    }
	    // ...
	}
	// ...
}
public class WeakReference<T> extends Reference<T> {
     
    public WeakReference(T referent, ReferenceQueue<? super T> q) {
     
        super(referent, q);  
    }  
    // ...
}
public abstract class Reference<T> {
     
    private T referent;         /* Treated specially by GC */  
    volatile ReferenceQueue<? super T> queue;
    // ...
}

expunge:清除
Stale:不新鲜的
Entries:条目(键值对)
失效条目指这样的条目:Entry(key=null, value=A)

get()、put()、size()等方法都会调用expungeStaleEntries()。expungeStaleEntries()会把队列中的所有引用取出。取出的Object x的实际类型是WeakHashMap.Entry,转换为Entry<K,V>类型后,可以从中取出强引用value,将value置null,并从Map中把该Entry去除(此处即使不让value=null,value指向的值对象应该也能被回收,因为Map不再持有该Entry -> Entry被回收 -> Entry中value曾指向的值对象被回收,只是得经过多次GC才行。所以手动value=null应该是为了使值对象更快回收?):

public class WeakHashMap<K,V> extends AbstractMap<K,V> implements Map<K,V> {
   
    // 清除所有失效的条目
    private void expungeStaleEntries() {
   
        // 轮询引用队列,移除所有条目
        for (Object x; (x = queue.poll()) != null; ) {
   
            // 同步队列以保证线程安全
            synchronized (queue) {
   
                @SuppressWarnings("unchecked")  // 抑制类型转换警告
                Entry<K,V> e = (Entry<K,V>) x;  // 将Object转换为Entry类型
                
                int i = indexFor(e.hash, 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,因为可能被迭代器使用
                        e.value = null;  // 清除值引用,帮助垃圾回收
                        size--; // 减少有效条目计数
                        break; 
                    }
                    // 移动到下一个节点继续查找
                    prev = p;
                    p 
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值