-
什么是内存泄漏
- 程序在申请内存后,无法释放已申请的内存空间
- 在定义变量时,需要一段内存空间来存储数据信息,而这段内存如果一直不被释放,那么就会导致内存被占用光,而被占用的这个对象,一直不能被回收掉,这就是内存泄漏
-
ThreadLocal
javaprivate ThreadLocalMap(ThreadLocalMap parentMap) { Entry[] parentTable = parentMap.table; int len = parentTable.length; setThreshold(len); table = new Entry[len]; for (int j = 0; j < len; j++) { Entry e = parentTable[j]; if (e != null) { @SuppressWarnings("unchecked") ThreadLocal<Object> key = (ThreadLocal<Object>) e.get(); if (key != null) { Object value = key.childValue(e.value); Entry c = new Entry(key, value); int h = key.threadLocalHashCode & (len - 1); while (table[h] != null) h = nextIndex(h, len); table[h] = c; size++; } } } }
- 每一个ThreadLocal维护一个ThreadLocalMap,key为使用弱引用的ThreadLocal实例,value为线程变量的副本
-
强引用
- 使用最普遍的引用,一个对象具有强引用,不会被垃圾回收器回收,当内存空间不足,JAVA虚拟机宁愿抛出OOM错误,使程序异常终止,也不会受这种对象
- 如果想取消强引用和某个对象之间的关联,可以显示地将引用赋值为null,这样可以使JVM在合适的时间就会回收该对象
-
弱引用
- JVM进行垃圾回收时,无论是内存是否充足,都会回收被弱引用关联的对象,在java中,用java.lang.ref.WeakReference类来表示,可以在缓存中使用弱引用
-
内存泄漏
- ThreadLocalMap使用ThreadLocal的弱应用作为key,如果一个ThreadLocal不存在外部强引用,Key势必会被GC回收,这样就导致ThreadLocal中key为null,而value还存在着强引用,只有Thread线程退出以后,value的强引用链条才会断掉
- 如果当前线程迟迟不结束,这些key为null的Entry的value就会一直存在一条强引用链
- Thread Ref -> Thread -> ThreadLocalMap ->Entry -> value
- 这个时候,永远无法回收,就会造成ThreadLocal出现内存泄漏的问题
-
如果ThreadLocalMap使用ThreadLocal的强引用
- 因为ThreadLocalMap还持有ThreadLocal的强引用,如果没有手动删除,ThreadLocal不会被回收,导致Entry内存泄漏
- 当ThreadLocalMap的key为弱引用回收ThreadLocal时,由于ThreadLocalMap持有ThreadLocal的弱引用,即使没有手动删除,ThreadLocal也会被回收,当key为null,在下一次ThreadLocalMap调用set和get,remove方法时会被清除value的值
-
为什么使用弱引用
-
因为使用弱引用可以多一层保障,弱引用ThreadLocal不会内存泄漏,对应的value在下一次ThreadLocalMap调用set,get,remove时会被清除
-
因此,ThreadLocal内存泄漏的根本原因是:由于ThreadLocalMap的生命周期跟Thread一样长,如果没有手动删除对应的key就会导致内存泄漏,而不是因为弱引用
javastatic class ThreadLocalMap { //hreadLocalMap中数据是存储在Entry类型数组的table中的,Entry继承了WeakReference(弱引用) static class Entry extends WeakReference<ThreadLocal<?>> { Object value; Entry(ThreadLocal<?> k, Object v) { super(k); value = v; } } **成员变量** //初始容量 private static final int INITIAL_CAPACITY = 16; //ThreadLocalMap数据真正存储在table中 private Entry[] table; //ThreadLocalMap条数 private int size = 0; //达到这个大小,则扩容 private int threshold;
-
构造函数
javaThreadLocalMap(ThreadLocal<?> firstKey, Object firstValue) { //初始化table数组,INITIAL_CAPACITY默认值为16 table = new Entry[INITIAL_CAPACITY]; //key和16取得哈希值 int i = firstKey.threadLocalHashCode & (INITIAL_CAPACITY - 1); //创建节点,设置key-value table[i] = new Entry(firstKey, firstValue); size = 1; //设置扩容阈值 setThreshold(INITIAL_CAPACITY); }
-
remove方法
javaprivate void remove(ThreadLocal<?> key) { Entry[] tab = table; int len = tab.length; int i = key.threadLocalHashCode & (len-1); //如果threadLocalHashCode计算出的下标找到的key和传入key不同,则证明出现哈希冲突,则循环向下查找 for (Entry e = tab[i]; e != null; e = tab[i = nextIndex(i, len)]) { //如果key相同 if (e.get() == key) { //删除当前Entry e.clear(); //清理 expungeStaleEntry(i); return; } } }
-
-
解决方法
- 每次使用完ThreadLocal都调用他的remove方法清除数据
- 将ThreadLocal变量定义为private static,这样就一直存在ThreadLocal的强引用,也就是保证任何时候都能通过ThreadLocal的弱引用访问到Entry的value值,进而清除掉
ThreadLocal的内存泄漏
莫得等待2024-07-10 17:04
相关推荐
重生之绝世牛码1 分钟前
Java设计模式 —— 【结构型模式】外观模式详解小蜗牛慢慢爬行7 分钟前
有关异步场景的 10 大 Spring Boot 面试问题新手小袁_J31 分钟前
JDK11下载安装和配置超详细过程呆呆小雅32 分钟前
C#关键字volatileMonly2133 分钟前
Java(若依):修改Tomcat的版本Ttang2335 分钟前
Tomcat原理(6)——tomcat完整实现钱多多_qdd1 小时前
spring cache源码解析(四)——从@EnableCaching开始来阅读源码waicsdn_haha1 小时前
Java/JDK下载、安装及环境配置超详细教程【Windows10、macOS和Linux图文详解】Q_19284999061 小时前
基于Spring Boot的摄影器材租赁回收系统Code_流苏1 小时前
VSCode搭建Java开发环境 2024保姆级安装教程(Java环境搭建+VSCode安装+运行测试+背景图设置)