ThreadLocal系列(三):用引用队列兜底内存泄漏

发布时间:2026/10/8 6:04:08
ThreadLocal系列(三):用引用队列兜底内存泄漏 引用队列思路兜底当GC回收了弱引用A、B所指向的对象a、b之后弱引用对象A和B会被加入到引用队列中ThreadLocalMap内部并没有使用ReferenceQueue它的清理依赖set/get/remove中的expungeStaleEntry。下面我们用自定义map来演示如果借助ReferenceQueue的思路也可以实现兜底清理。自定义map自定义一个mapkey是弱引用value是强引用然后调用GC回收看下面的内容public class TestWeak { static ReferenceQueueObject queue new ReferenceQueue(); static class WeakMap { static class Entry extends WeakReferenceString { String value; public Entry(String key, String value) { super(key); this.value value; } } Entry[] table new Entry[5]; public void put(int index, String key, String value) { table[index] new Entry(key, value); } public String get(String key) { for (Entry entry : table) { if (entry ! null) { String k entry.get(); if (k ! null k.equals(key)) { return entry.value; } } } return null; } Override public String toString() { StringBuilder sb new StringBuilder([); boolean first true; for (Entry entry : table) { if (entry ! null) { // 这个get是WeakReference的父的Reference的get // get返回的就是super(key)的key String k entry.get(); if (!first) { sb.append(,); } if (k ! null) { sb.append(k).append(:).append(entry.value); } else { sb.append(null).append(:).append(entry.value); } first false; } } sb.append(]); return sb.toString(); } } public static void main(String[] args) { WeakMap weakMap new WeakMap(); weakMap.put(0, new String(a), 0); weakMap.put(1, new String(b), 1); weakMap.put(2, new String(c), 2); weakMap.put(3, new String(d), 3); weakMap.put(4, new String(e), 4); System.out.println(weakMap); // System.gc(); // System.out.println(weakMap.get(a)); // System.out.println(weakMap.get(b)); // System.out.println(weakMap.get(c)); // System.out.println(weakMap.get(d)); // System.out.println(weakMap.get(e)); // System.out.println(weakMap); } }下面是没调用GC和调用GC的结果没调用GC[a:0,b:1,c:2,d:3,e:4]调用GC去掉下面注释[a:0,b:1,c:2,d:3,e:4] null null null null null [null:0,null:1,null:2,null:3,null:4]可以看到GC之后弱引用对象没了但是value还在加入引用队列在Entry构造方法的super中还能传一个queue参数可以看到GC回收a之后a的弱引用A会被加入到引用队列里public class TestWeak { static ReferenceQueueObject queue new ReferenceQueue(); static class WeakMap{ static class Entry extends WeakReferenceString{ String value; public Entry(String key, String value){ // super(key); //这里的queue里放入的是entry对象 super(key, queue); this.value value; } } public void cleanQueue(){ Object object; while ((object queue.poll()) ! null){ System.out.println(队列里有对象 object); } } Entry[] table new Entry[5]; public void put(int index, String key, String value){ table[index] new Entry(key, value); } public String get(String key){ for (Entry entry : table) { if (entry ! null){ String k entry.get(); if (k ! null k.equals(key)){ return entry.value; } } } return null; } Override public String toString() { StringBuilder sb new StringBuilder([); boolean first true; for (Entry entry : table) { if (entry ! null) { // 这个get是WeakReference的父的Reference的get // get返回的就是super(key)的key String k entry.get(); if (!first) { sb.append(,); } if (k ! null) { sb.append(k).append(:).append(entry.value); } else { sb.append(null).append(:).append(entry.value); } first false; } } sb.append(]); return sb.toString(); } } public static void main(String[] args) { WeakMap weakMap new WeakMap(); weakMap.put(0, new String(a), 0); weakMap.put(1, new String(b), 1); weakMap.put(2, new String(c), 2); weakMap.put(3, new String(d), 3); weakMap.put(4, new String(e), 4); System.out.println(weakMap); System.gc(); System.out.println(weakMap.get(a)); System.out.println(weakMap.get(b)); System.out.println(weakMap.get(c)); System.out.println(weakMap.get(d)); System.out.println(weakMap.get(e)); System.out.println(weakMap); weakMap.cleanQueue(); } }运行结果[a:0,b:1,c:2,d:3,e:4] null null null null null [null:0,null:1,null:2,null:3,null:4] 队列里有对象threadlocal.TestWeak$WeakMap$Entry1d44bcfa 队列里有对象threadlocal.TestWeak$WeakMap$Entry266474c2 队列里有对象threadlocal.TestWeak$WeakMap$Entry6f94fa3e 队列里有对象threadlocal.TestWeak$WeakMap$Entry5e481248 队列里有对象threadlocal.TestWeak$WeakMap$Entry66d3c617Entry对key是弱引用对value是强引用GC之后弱引用所指向的对象abcde没了而entry对象即弱引用本身被放入了引用队列里所以可以对引用队列里的entry对象进行处理断开entry对value的强引用关系这样就可以回收value了public class TestWeak { static ReferenceQueueObject queue new ReferenceQueue(); static class WeakMap{ static class Entry extends WeakReferenceString{ String value; public Entry(String key, String value){ // super(key); //这里的queue里放入的是entry对象 super(key, queue); this.value value; } } public void cleanQueue(){ Object object; while ((object queue.poll()) ! null){ System.out.println(队列里有对象 object); for (int i 0; i table.length; i) { // 将table里的所有entry和queue里现在拿到的entry对比 // 如果这俩同一个对象就把整个entry干掉 //原来 //table[i] ──强──→ Entry ──弱──→ key // │ // └──强──→ value // table[i] null之后 //table[i] → null // Entry ──弱──→ key已被GC // Entry ──强──→ value // 没人引用entry了entry被回收Entry被回收导致没人引用value了value被回收 if (table[i] object){ table[i] null; } } } } Entry[] table new Entry[5]; public void put(int index, String key, String value){ table[index] new Entry(key, value); } public String get(String key){ for (Entry entry : table) { if (entry ! null){ String k entry.get(); if (k ! null k.equals(key)){ return entry.value; } } } return null; } Override public String toString() { StringBuilder sb new StringBuilder([); boolean first true; for (Entry entry : table) { if (entry ! null) { // 这个get是WeakReference的父的Reference的get // get返回的就是super(key)的key String k entry.get(); if (!first) { sb.append(,); } if (k ! null) { sb.append(k).append(:).append(entry.value); } else { sb.append(null).append(:).append(entry.value); } first false; } } sb.append(]); return sb.toString(); } } public static void main(String[] args) { WeakMap weakMap new WeakMap(); weakMap.put(0, new String(a), 0); weakMap.put(1, new String(b), 1); weakMap.put(2, new String(c), 2); weakMap.put(3, new String(d), 3); weakMap.put(4, new String(e), 4); System.out.println(weakMap); System.gc(); System.out.println(weakMap.get(a)); System.out.println(weakMap.get(b)); System.out.println(weakMap.get(c)); System.out.println(weakMap.get(d)); System.out.println(weakMap.get(e)); System.out.println(weakMap); weakMap.cleanQueue(); System.out.println(weakMap); } }运行结果[a:0,b:1,c:2,d:3,e:4] null null null null null [null:0,null:1,null:2,null:3,null:4] 队列里有对象threadlocal.TestWeak$WeakMap$Entry1d44bcfa 队列里有对象threadlocal.TestWeak$WeakMap$Entry266474c2 队列里有对象threadlocal.TestWeak$WeakMap$Entry6f94fa3e 队列里有对象threadlocal.TestWeak$WeakMap$Entry5e481248 队列里有对象threadlocal.TestWeak$WeakMap$Entry66d3c617 []当然在**weakMap.put(0, new String(a), 0);**中是new了个String如果直接把a“放进去了public class TestWeak { static ReferenceQueueObject queue new ReferenceQueue(); static class WeakMap { static class Entry extends WeakReferenceString { String value; public Entry(String key, String value) { // super(key); //这里的queue里放入的是entry对象 super(key, queue); this.value value; } } public void cleanQueue() { Object object; while ((object queue.poll()) ! null) { System.out.println(队列里有对象 object); for (int i 0; i table.length; i) { // 将table里的所有entry和queue里现在拿到的entry对比 // 如果这俩同一个对象就把整个entry干掉 //原来 //table[i] ──强──→ Entry ──弱──→ key // │ // └──强──→ value // table[i] null;之后 //table[i] → null // Entry ──弱──→ key已被GC // Entry ──强──→ value // 没人引用entry了entry被回收Entry被回收导致没人引用value了value被回收 if (table[i] object) { table[i] null; } } } } Entry[] table new Entry[5]; public void put(int index, String key, String value) { table[index] new Entry(key, value); } public String get(String key) { for (Entry entry : table) { if (entry ! null) { String k entry.get(); if (k ! null k.equals(key)) { return entry.value; } } } return null; } Override public String toString() { StringBuilder sb new StringBuilder([); boolean first true; for (Entry entry : table) { if (entry ! null) { // 这个get是WeakReference的父的Reference的get // get返回的就是super(key)的key String k entry.get(); if (!first) { sb.append(,); } if (k ! null) { sb.append(k).append(:).append(entry.value); } else { sb.append(null).append(:).append(entry.value); } first false; } } sb.append(]); return sb.toString(); } } public static void main(String[] args) { WeakMap weakMap new WeakMap(); // weakMap.put(0, new String(a), 0); // weakMap.put(1, new String(b), 1); // weakMap.put(2, new String(c), 2); // weakMap.put(3, new String(d), 3); // weakMap.put(4, new String(e), 4); weakMap.put(0, a, 0); weakMap.put(1, b, 1); weakMap.put(2, c, 2); weakMap.put(3, d, 3); weakMap.put(4, e, 4); System.out.println(weakMap); System.gc(); System.out.println(weakMap.get(a)); System.out.println(weakMap.get(b)); System.out.println(weakMap.get(c)); System.out.println(weakMap.get(d)); System.out.println(weakMap.get(e)); System.out.println(weakMap); weakMap.cleanQueue(); System.out.println(weakMap); } }运行结果[a:0,b:1,c:2,d:3,e:4] 0 1 2 3 4 [a:0,b:1,c:2,d:3,e:4] [a:0,b:1,c:2,d:3,e:4]原因new String(a):堆上new出来的a → 没有强引用 → GC回收 → entry.get()null现在用“a”字符串常量池 ──强引用──→ a → 有强引用 → GC不回收 → entry.get()a再调用get方法在for里找就找到了对应的0。以上就是从ReferenceQueue的角度思考如何防范OOM相当于remove这更是一个兜底措施。

关于本文作者

来自尧图内容编辑团队

尧图内容编辑团队 内容团队

尧图内容编辑团队

本文由尧图网络内容编辑团队执笔。团队由资深项目经理、前端工程师与设计师组成,所有内容均来自亲手交付的真实项目,先讲清问题、再给出可落地的解法。尧图深耕北京网站建设十年,服务过京华建材集团、智造科技等各行业客户,把一线经验沉淀为可复用的行业观察。

  • 十年建站经验,覆盖建材、制造、服务、文创等
  • 项目经理把关选题与事实准确性
  • 工程师与设计师联合撰写专业细节
  • 统一编辑规范,保证文风与排版一致
  • 每月复盘转化数据,迭代选题方向

延伸阅读

相关资讯与近期热门内容

深度阅读推荐

建站决策前值得细读的三篇

网站改版的5个关键决策
2024-08-12

网站改版的5个关键决策

什么时候该改版、改到什么程度、如何避免流量掉光,京华建材集团改版复盘给出答案。

获取专属建站方案

看完文章,把您的行业与预算告诉我们,免费获取一份量身定制的官网建设方案与报价。

立即免费咨询