ThreadLocal为什么会导致内存泄漏?

ThreadLocal内存泄漏原因详解 ThreadLocal泄漏的原因详解 阅读详情
AI摘要

本文详细介绍了ThreadLocal可能导致内存泄漏的原因,分析了Java的四种引用类型,特别是弱引用在ThreadLocalMap中的作用。强调ThreadLocal并非内存泄漏的元凶,反而通过弱引用设计帮助避免内存泄漏。同时,解释了ThreadLocal在set、get和remove操作中如何清理过期节点,以及如何正确使用ThreadLocal以防止内存泄漏。

目录

1. 前言

1.1 何为内存泄漏?

1.2 ThreadLocal介绍

2. 内存泄漏的原因

Java的四种引用

2.1 弱引用是原罪吗?

2.2 如何避免内存泄漏?

2.3 ThreadLocal做出的努力

2.3.1、set()的清理逻辑

2.3.2、get()的清理逻辑

2.3.3、remove()的清理逻辑

内存泄漏的根本原因

1. 前言

“ThreadLocal为什么会导致内存泄漏,如何避免?”

我们通过一张图来清楚地表示ThreadLocal的引用关系

在这里插入图片描述

1.1 何为内存泄漏?

首先我们有必要了解,到底何为「内存泄漏」?笔者这里引用百度百科的解释。

内存泄漏(Memory Leak)是指程序中已动态分配的堆内存由于某种原因程序未释放或无法释放,造成系统内存的浪费,导致程序运行速度减慢甚至系统崩溃等严重后果。

站在Java的角度来说,就是JVM创建的对象永远都无法访问到,但是GC又不能回收对象所占用的内存。少量的内存泄漏并不会出现什么严重问题,无非是浪费了一些内存资源罢了,但是随着时间的积累,内存泄漏的越来越多就会导致「内存溢出」,程序崩溃。

因此,开发者必须非常小心,尽量避免内存泄漏,一旦发现就要尽快解决,以免造成严重后果。

1.2 ThreadLocal介绍

本篇文章主要记录ThreadLocal内存泄漏的原因,但是怕部分读者可能还不太了解ThreadLocal,所以还是决定再稍微介绍一下。

多个线程访问同一个共享变量时,如果不做同步控制,往往会出现「数据不一致」的问题,通常会使用synchronized关键字加锁来解决,ThreadLocal则换了一个思路。

ThreadLocal本身并不存储值,它依赖于Thread类中的ThreadLocalMap,当调用set(T value)时,ThreadLocal将自身作为Key,值作为Value存储到Thread类中的ThreadLocalMap中,这就相当于所有线程读写的都是自身的一个私有副本,线程之间的数据是隔离的,互不影响,也就不存在线程安全问题了。

2. 内存泄漏的原因

ThreadLocalMap内部维护了一个Entry[] table来存储键值对的映射关系,内存泄漏和Entry类有非常大的关系,下面是Entry的源码:

static class Entry extends WeakReference<ThreadLocal<?>> {
    /** The value associated with this ThreadLocal. */
    Object value;
    Entry(ThreadLocal<?> k, Object v) {
        super(k);
        value = v;
    }
}

Entry将ThreadLocal作为Key,值作为value保存,它继承自WeakReference,注意构造函数里的第一行代码super(k),这意味着ThreadLocal对象是一个「弱引用」。有的同学可能对「弱引用」不太熟悉,这里再介绍一下Java的四种引用关系。

Java的四种引用

在JDK1.2之前,“引用”的概念过于狭隘,如果Reference类型的数据存储的是另外一块内存的起始地址,就称该Reference数据是某块地址、对象的引用,对象只有两种状态:被引用、未被引用。
这样的描述未免过于僵硬,对于这一类对象则无法描述:内存足够时暂不回收,内存吃紧时进行回收。例如:缓存数据。

在JDK1.2之后,Java对引用的概念做了一些扩充,将引用分为四种,由强到弱依次为:

强引用(Strongly Reference)
指代码中普遍存在的赋值行为,如:Object o = new Object(),只要强引用关系还在,对象就永远不会被回收。
软引用(Soft Reference)
还有用处,但是非必须存活的对象,JVM会在内存溢出前对其进行回收,例如:缓存。
弱引用(Weak Reference)
非必须存活的对象,引用关系比软引用还弱,不管内存是否够用,下次GC一定回收。
虚引用(Phantom Reference)
也称“幽灵引用”、“幻影引用”,最弱的引用关系,完全不影响对象的回收,等同于没有引用,虚引用的唯一的目的是对象被回收时会收到一个系统通知。
综上所述,由于ThreadLocal对象是弱引用,如果外部没有强引用指向它,它就会被GC回收,导致Entry的Key为null,如果这时value外部也没有强引用指向它,那么value就永远也访问不到了,按理也应该被GC回收,但是由于Entry对象还在强引用value,导致value无法被回收,这时「内存泄漏」就发生了,value成了一个永远也无法被访问,但是又无法被回收的对象。

Entry对象属于ThreadLocalMap,ThreadLocalMap属于Thread,如果线程本身的生命周期很短,短时间内就会被销毁,那么「内存泄漏」立刻就会得到解决,只要线程被销毁,value也会随之被回收。问题是,线程本身是非常珍贵的计算机资源,很少会去频繁的创建和销毁,一般都是通过线程池来使用,这就将线程的生命周期大大拉长,「内存泄漏」的影响也会越来越大。

2.1 弱引用是原罪吗?

网上有的文章将ThreadLocal内存泄漏的原因怪罪于Entry的Key的弱引用,这个说法是极其错误的!

不用弱引用就能避免「内存泄漏」了吗?当然不是!!!
恰恰相反,使用弱引用是JDK在尽量避免程序出现「内存泄漏」,如下代码:

public class Test {
    public static void main(String[] args) {
        ThreadLocal threadLocal = new ThreadLocal();
        threadLocal.set(new Object());
        threadLocal = null;
    }
}

创建一个ThreadLocal对象,并设置一个Object对象,然后将其置空。如果Key不是弱引用的话,threadLocal无法被回收,也无法被访问,object无法被回收,也无法被访问,Key和Value同时出现了「内存泄漏」。

当Key是弱引用时,threadLocal由于外部没有强引用了,GC可以将其回收,ThreadLocal通过key.get()==null可以判断Key已经被回收了,当前Entry是一个废弃的过期节点,因此ThreadLocal可以自发的清理这些过期节点,来避免「内存泄漏」。

ThreadLocalMap是一个容器,不可能只进不出,否则时间长了必然会导致「内存溢出」,这也是大家平时使用各种容器对象时需要注意的点!ThreadLocal通过弱引用技术,可以及时发现过期的节点并清理,因此,弱引用是ThreadLocal来避免「内存泄漏」的,而不是导致内存泄漏的元凶。

2.2 如何避免内存泄漏?

使用ThreadLocal时,一般建议将其声明为static final的,避免频繁创建ThreadLocal实例。
尽量避免存储大对象,如果非要存,那么尽量在访问完成后及时调用remove()删除掉。

2.3 ThreadLocal做出的努力

ThreadLocal不是洪水猛兽,不要听到「内存泄漏」就不敢使用它,只要你规范化使用是不会有问题的。再者,就算你不规范使用,ThreadLocal也做出了很多努力来最大程度的帮你避免发生「内存泄漏」。

前面已经说过,由于Key是弱引用,因此ThreadLocal可以通过key.get()==null来判断Key是否已经被回收,如果Key被回收,就说明当前Entry是一个废弃的过期节点,ThreadLocal会自发的将其清理掉。

ThreadLocal会在以下过程中清理过期节点:

调用set()方法时,采样清理、全量清理,扩容时还会继续检查。
调用get()方法,没有直接命中,向后环形查找时。
调用remove()时,除了清理当前Entry,还会向后继续清理。

2.3.1、set()的清理逻辑

当线程调用ThreadLocal.set(T value)时,它会将ThreadLocal对象作为Key,值作为value设置到ThreadLocalMap中,源码如下:

private void set(ThreadLocal<?> key, Object value) {
    Entry[] tab = table;
    int len = tab.length;
    // 计算下标,算法:hashCode & (len - 1),和HashMap一样,这里不详叙。
    int i = key.threadLocalHashCode & (len-1);

    for (Entry e = tab[i];
         /*
         如果下标元素不是null,有两种情况:
         1.同一个Key,覆盖value。
         2.哈希冲突了。
          */
         e != null;
         /*
         哈希冲突的解决方式:开放定址法的线性探测。
         当前下标被占用了,就找next,找到尾巴还没找到就从头开始找。
         直到找到没有被占用的下标。
          */
         e = tab[i = nextIndex(i, len)]) {
        ThreadLocal<?> k = e.get();

        if (k == key) {
            // 相同的Key,则覆盖value。
            e.value = value;
            return;
        }

        if (k == null) {
            /*
            下标被占用,但是Key.get()为null。说明ThreadLocal被回收了。
            需要进行替换。
             */
            replaceStaleEntry(key, value, i);
            return;
        }
    }

    tab[i] = new Entry(key, value);
    int sz = ++size;
    /*
    1.判断是否可以清理一些槽位。
    2.如果清理成功,就无需扩容了,因为已经腾出一些位置留给下次使用。
    3.如果清理失败,则要判断是否需要扩容。
     */
    if (!cleanSomeSlots(i, sz) && sz >= threshold)
        rehash();
}

如果Entry.get()==null说明发生哈希冲突了,且旧Key已经被回收了,此时ThreadLocal会替换掉旧的value,避免发生「内存泄漏」。
如果没有哈希冲突,Thr eadLocal仍然会调用cleanSomeSlots来清理部分节点,源码如下:

/*
清理部分槽位。
1.如果清理成功,就不用扩容了,因为已经腾出一部分位置了。
2.出于性能考虑,不会做所有元素做清理工作,而是采样清理。
set()时,n=size,搜索范围较小。
 */
private boolean cleanSomeSlots(int i, int n) {
    boolean removed = false;
    Entry[] tab = table;
    int len = tab.length;
    do {
        i = nextIndex(i, len);
        Entry e = tab[i];
        if (e != null && e.get() == null) {
            // 一旦搜索到了过期元素,则n=len,扩大搜索范围
            n = len;
            removed = true;
            // 真正清理的逻辑
            i = expungeStaleEntry(i);
        }
        /*
        采样规则: n >>>= 1 (折半)
        例:100 > 50 > 25 > 12 > 6 > 3 > 1
         */
    } while ( (n >>>= 1) != 0);
    return removed;
}

真正的清理逻辑在expungeStaleEntry()中,源码如下:

/*
删除过期的元素:占用下标,但是ThreadLocal实例已经被回收的元素。
 */
private int expungeStaleEntry(int staleSlot) {
    Entry[] tab = table;
    int len = tab.length;

    // 清理当前Entry
    tab[staleSlot].value = null;
    tab[staleSlot] = null;
    size--;

    // Rehash until we encounter null
    Entry e;
    int i;
    // 继续往后寻找,直到遇到null结束
    for (i = nextIndex(staleSlot, len);
         (e = tab[i]) != null;
         i = nextIndex(i, len)) {
        ThreadLocal<?> k = e.get();
        if (k == null) {
            // 再次发现过期元素,清理掉
            e.value = null;
            tab[i] = null;
            size--;
        } else {
            // 处理重新哈希的逻辑
            int h = k.threadLocalHashCode & (len - 1);
            if (h != i) {
                tab[i] = null;

                // Unlike Knuth 6.4 Algorithm R, we must scan until
                // null because multiple entries could have been stale.
                while (tab[h] != null)
                    h = nextIndex(h, len);
                tab[h] = e;
            }
        }
    }
    return i;
}

清理时,并不是只清理掉当前Entry就结束了,而是会往后环形的继续寻找过期的Entry,只要找到了就清理,直到遇到tab[i]==null就结束,清理的过程中还会对元素做一个rehash的操作。

2.3.2、get()的清理逻辑

线程调用ThreadLocal.get()时,会从ThreadLocalMap.getEntry(this)去查找,源码如下:

/*
通过Key获取Entry
 */
private Entry getEntry(ThreadLocal<?> key) {
    // 计算下标
    int i = key.threadLocalHashCode & (table.length - 1);
    Entry e = table[i];
    if (e != null && e.get() == key) {
        // 如果对应下标节点不为null,且Key相等,则命中直接返回
        return e;
    } else {
        /*
        否则有两种情况:
        1.Key不存在。
        2.哈希冲突了,需要向后环形查找。
         */
        return getEntryAfterMiss(key, i, e);
    }
}

如果命中则直接返回,如果没有命中则可能是哈希冲突了、或者Key不存在/已被回收,接着调用getEntryAfterMiss()查找,这里也会进行过期节点的清理:

/*
无法直接命中的查找逻辑
 */
private Entry getEntryAfterMiss(ThreadLocal<?> key, int i, Entry e) {
    Entry[] tab = table;
    int len = tab.length;

    while (e != null) {// e==null说明Key不存在,直接返回null
        ThreadLocal<?> k = e.get();
        if (k == key)
            // 找到了,说明是哈希冲突
            return e;
        if (k == null)
            // Key存在,但是过期了,需要清理掉,并且返回null
            expungeStaleEntry(i);
        else
            // 向后环形查找
            i = nextIndex(i, len);
        e = tab[i];
    }
    return null;
}

2.3.3、remove()的清理逻辑

线程调用ThreadLocal.remove()本身就是清理当前节点的,但是为了避免发生「内存泄漏」,ThreadLocal还会检查容器中是否还有其他过期节点,如果发现也会一并清理,主要逻辑在ThreadLocalMap.remove()中:

// 通过Key删除Entry
private void remove(ThreadLocal<?> key) {
    Entry[] tab = table;
    int len = tab.length;
    // 计算下标
    int i = key.threadLocalHashCode & (len-1);
    /*
    删除也是一样,由于存在哈希冲突,不能直接定位到下标后直接删除。
    删除前需要确认Key是否相等,如果不等需要往后环形查找。
     */
    for (Entry e = tab[i];
         e != null;
         e = tab[i = nextIndex(i, len)]) {
        if (e.get() == key) {
            /*
            找到了就清理掉。
            这里并没有直接清理,而是将Key的Reference引用清空了,
            然后再调用expungeStaleEntry()清理过期元素。
            顺便还可以清理后续节点。
             */
            e.clear();
            expungeStaleEntry(i);
            return;
        }
    }
}

内存泄漏的根本原因

所有Entry对象都被ThreadLocalMap类的实例化对象threadLocals持有,当ThreadLocal对象不再使用时,ThreadLocal对象在栈中的引用就会被回收,一旦没有任何引用指向ThreadLocal对象,Entry只持有弱引用的key就会自动在下一次YGC时被回收,而此时持有强引用的Entry对象并不会被回收。
简而言之: threadLocals对象中的entry对象不在使用后,没有及时remove该entry对象 ,然而程序自身也无法通过垃圾回收机制自动清除,从而导致内存泄漏。

转载自https://www.cnblogs.com/cy0628/p/15086201.html,侵删。

JAVA|一步一步带你看清,关于ThreadLocal内存溢出代码演示和原因分析! ThreadLocal 翻译成中文是线程本地变量的意思,也就是说它是线程中的私有变量,每个线程只能操作自己的私有变量,所以不会造成线程不安全的问题。 线程不安全是指,多个线程在同一时刻对同一个全局变量做写操作时(读操作不会涉及线程不安全问题),如果执行的结果和我们预期的结果不一致就称之为线程不安全,反之,则称为线程安全。 在 Java 语言中解决线程不安全的问题通常有两种手段: 1.使用锁(使用 synchronized 或 Lock); 2.使用 ThreadLocal。 锁的实现方案是在多线程写入全局. 阅读详情

相关推荐

为什么ThreadLocal会导致内存泄漏?如何正确使用?

摘要: 一次生产环境内存泄漏排查案例,因ThreadLocal未调用remove()导致内存从2GB飙升至8GB。分析发现线程池复用线程时,ThreadLocal中存储的User对象未被清理,导致数据累积最终OOM。解决方案包括finally块清理和拦截器统一清理,推荐后者更可靠。优化后内存降低75%,GC频率减少80%,响应时间提升20倍。核心教训:ThreadLocal必须配套remove()使用,避免存储大对象,异步场景需特殊处理。小小的remove()调用能防止严重内存泄漏问题。

潘子的博客 457

threadlocal内存泄漏的原因

ThreadLocal的原理: ThreadLocal的set实际实在当前线程对象里创建了一个内部变量ThreadLocalMap<ThreadLocal,object> ,ThreadLocalMap的key是ThreadLocal的引用。 造成泄漏的原因: 由于ThreadLocal对象是弱引用,如果外部没有强引用指向它,它就会被GC回收,导致Entry的Key为null 如果当前的情况下在栈中将threadlocal1的引用设置为null,强引用1将会失效,那堆中的thre..

oliver486的博客 9062

OpenFOAM-v2006重叠网格挖洞问题实战:如何手动修复overset代码中的边缘识别缺陷

本文深入探讨了OpenFOAM-v2006中重叠网格挖洞问题的实战解决方案,重点修复overset代码中的边缘识别缺陷。通过逆向工程解析挖洞决策机制,设计最小侵入式的函数override方案,并建立多级验证体系,有效解决了网格挖洞区域覆盖不全的问题,提升了计算流体动力学模拟的准确性和可靠性。

weixin_29216049的博客 429

图文Debug详解 ThreadLocal内存泄漏的原因

一、前言 在分析ThreadLocal导致的内存泄露前,需要了解一下内存泄露、强引用与弱引用以及GC回收机制。这样才能更好分析ThreadLocal泄漏的原因。 如果了解上述问题可直接跳到第二节。 1.1 内存泄漏 如果不会被使用的对象或者变量占用的内存不能被回收,就是内存泄漏。如果泄漏的数据量足够大,可能会引起内存溢出,导致程序异常结束。 1.2 强引用与弱引用 强引用: 如 String name = new String(); 一个对象具有强引用,不会被垃圾回收器回收。当内存空间不足,Java虚拟机

qq_40205337的博客 5350

基于LightGBM和多模型融合的量化交易研究:沪深300为例【附代码】

📊 数据科学与大数据专业 | 数据分析与模型构建 | 数据驱动决策✨ 专业领域:数据挖掘与清洗大数据处理与存储技术机器学习与深度学习模型数据可视化与报告生成分布式计算与云计算数据安全与隐私保护💡 擅长工具:Python/R/Matlab 数据分析与建模Hadoop/Spark 大数据处理平台SQL数据库管理与优化Tableau/Power BI 数据可视化工具TensorFlow/PyTorch 深度学习框架✅数据科学与大数据技术毕业论文【数据】

lagougongzuoshi的博客 1669

如何实现chrome谷歌浏览器多开(独立环境 独立cookie)

由于各种各样的原因,你可能需要在一个电脑登录某个平台,比如一个电脑登录3个公众号,或者3个知乎等等。最简单的方案是,直接安装3个不同的浏览器,比如一个谷歌浏览器,一个火狐浏览器,一个 Edge 浏览器。这个方案也能解决,但如果想多开10个呢?安装10个不同的浏览器也能解决,但感觉这样比较复杂,另外一方面安装太多的应用,会导致电脑卡顿。这里给大家介绍,我在 Win 和 Mac 系统下的躲开解决方案。Win 系统下的解决方案最为简单,原理就是给不同的谷歌浏览器,指向不同的用户存储路径即可。

lzqial1987的专栏 1万+

ThreadLocal 内存泄露问题是怎么导致的?

实例(作为 key)已经没有强引用指向它们,但由于 value 对象仍然有其他地方的强引用,那么即使线程结束了,这些 value 对象也无法被垃圾回收,从而可能导致内存泄漏。尽管 Java 8 引入了弱引用以减少内存泄漏的可能性,但在某些特定场景下,如循环依赖、忘记清理等,还是可能会发生内存泄漏。的引用,那么它就无法被垃圾回收,同时与其关联的 value 也可能因为强引用而无法回收,造成内存泄漏。为了避免 ThreadLocal 导致的内存泄漏,建议在使用完毕或线程结束时,显式地调用。实例的引用是强引用。

学亮编程手记 1216

动力锂电池-二阶RC模型等效电路-RLS参数辨识-Simulink

1.锂电池二阶RC等效电路模型

QQ1978009424的博客 1万+

关于ThreadLocal,为什么会导致内存泄漏?

首先来看ThreadLocal中的源码,这是ThreadLocal中的set方法 它首先通过Thread.currentThread()方法去获取当前线程,然后将当前线程t传到getMap方法里面。 跟进去getMap方法之后我们发现,t.threadLocals调用的是其实线程Thread类里面的一个局部变量ThreadLocal.ThreadLocalMap。 也就是说每个Thread线程都有自己独有的一个Map叫ThreadLocalMap,这也就是为什么ThreadLocal能保证线程互不干扰.

qq_42554859的博客 467

华为手环实时心率数据导出到UWP项目中

hello大家好呀O(∩_∩)O最近在做毕设 其中有个小功能要用到实时心率数据。查找网上的资料发现只有小米手环的实时心率数据导出但是没有华为手环的,我试了华为手环但是并不能行得通哈哈哈。现在分享一下华为手环实时心率数据导出的代码。

apple_63713858的博客 1581

ECU诊断功能测试:从理论到实践

ECU诊断功能是指通过特定的诊断协议与ECU进行通信,以获取ECU的状态信息、故障码、数据流等,并执行特定的诊断操作。诊断功能的主要目的是帮助开发人员和维修人员快速定位和解决ECU中的问题,确保车辆的安全性和可靠性。UDS协议是ISO 14229标准的一部分,定义了一系列的诊断服务,如读取故障码、清除故障码、读取数据流等。UDS协议基于CAN(Controller Area Network)总线,具有较高的灵活性和扩展性。读取故障码是诊断功能中最基本的操作之一。通过读取故障码,可以快速定位ECU中的问题。

闲人编程的博客 1804

ThreadLocal为什么会导致内存泄漏

如何使用removefinallyremove。

晴天飛 雪的博客 496

泰山派rk3566 audio音频基础介绍

本文介绍了嵌入式系统中音频处理的基础知识,重点围绕RK3566平台的音频子系统展开。文章首先解释了音频信号从模拟到数字的转换过程,包括采样率、位深和声道等核心概念。随后详细分析了嵌入式音频开发中的关键接口:I2C用于控制音频编解码器,I2S作为专用音频数据传输总线,以及PCM作为原始音频格式和ALSA设备抽象的双重含义。通过对这些基础概念和接口的解析,为在RK3566平台上进行音频驱动开发和应用调试提供了理论基础。

qq_37077309的博客 294

为什么 ThreadLocal 会导致内存泄漏 ?如何解决 ?

为什么 ThreadLocal 会导致内存溢出 ?如何解决 ?

xaiobit_hl的博客 465

揭秘LLM如何预测下一个词

Transformer结构的大语言模型(LLM)通过自回归训练学习预测下一个词汇,其核心能力源自权重矩阵的动态优化。自注意力层的Wq/Wk/Wv矩阵捕捉语义关联,前馈网络的W1/W2矩阵进行非线性特征提取,LM Head的W_{lm}矩阵将语义映射到词汇概率。训练过程中,这些矩阵通过反向传播不断调整,最终使模型能够根据上下文(如"我"→"爱")生成合理的后续词汇。这种基于矩阵变换的数学机制是LLM智能生成文本的本质原因。(150字)

ZhangJiqun&191cm&85kg 376

Camera2Demo

在Camera2 的基础上进行封装,使得Camera2一堆初始化逻辑与Activity分离,降低耦合度,完成预览拍照并保存的操作

面试官:你说说ThreadLocal为什么会导致内存泄漏?

1. 前言 “ThreadLocal为什么会导致内存泄漏,如何避免?” 这是笔者在面试阿里时,面试官提出的问题,当时回答的并不好,今天刚好有时间,决定复盘一下,彻底弄清楚内存泄漏的原因,并分享给大家。 1.1 何为内存泄漏? 首先我们有必要了解,到底何为「内存泄漏」?笔者这里引用百度百科的解释。 内存泄漏(Memory Leak)是指程序中已动态分配的堆内存由于某种原因程序未释放或无法释放,造成系统内存的浪费,导致程序运行速度减慢甚至系统崩溃等严重后果。 站在Java的角度来说,就是JVM创建的对象永远

程序员小潘 3851

WINCC7.3破解文件

WINCC7.3硬件够破解文件,有需要的下载!WINCC7.3硬件够破解文件,有需要的下载!

Java中的ThreadLocal为什么会导致内存泄漏?如何解决的?

InheritableThreadLocal 可以在**子线程中继承父线程中的值**。在创建子线程时,子线程将复制父线程中的 InheritableThreadLocal 变量。InheritableThreadLocal是用于主子线程之间参数传递的,但必须要是在主线程中手动创建的子线程才可以,很多时候线程都是通过线程池进行创建和复用的,这时候InheritableThreadLocal就不行了。TransmittableThreadLocal继承并加强InheritableThreadLocal类。

zzh_zzh_zxm的博客 2150

蓝牙模块HM-10手册

蓝牙模块HM-10手册,该手册对蓝牙模块HM-10进行了详细的介绍和使用方法以及一些AT指令。

文档:亲测可用解决vnc灰屏,黑屏,鼠标大黑叉情况

主要解决vnc-viewer灰屏,黑屏,鼠标大黑叉等现象,详细见文档。

Java - 为什么 ThreadLocal 会导致内存泄漏

如果 ThreadLocal 没有被直接引用(外部强引用),那么在垃圾回收的时候,由于 ThreadLocalMap 中的 key 是弱引用,所以 key 一定会被回收,因此 ThreadLocalMap 中就会出现 key 为 null 的 Entry,并且没有办法访问这些数据,那么强引用链 Thread -> ThreadLocalMap -> Entry -> value 就会一直存在,导致 value 无法被 GC 回收,从而导致内存泄漏。为什么是一直持有 value?

m0_65228708的博客 297

系列七、ThreadLocal为什么会导致内存泄漏

ThreadLocalMap是ThreadLocal的内部类,没有实现Map接口,用独立的方式实现了Map的功能,其内部的Entry也是独立实现的。

仰望星空,脚踏实地!安全感是自己给的,努力才是一辈子的安全感。 1372

ThreadLocal为什么会导致内存泄漏(详细讲解)

key 被回收后,Entry 里的 key 为 null,但 Entry 本身仍被 ThreadLocalMap 引用,而 value 是 Entry 的普通强引用,因此 value 依然可达,不会被回收。现在对threadLocal内存泄漏的原理是不是会清晰一点。

2201_75420324的博客 1117

ThreadLocal-为什么会导致内存泄漏

在使用threadLocal之后,需要使用remove进行移除即可。

路漫漫其修远兮 吾将上下而求索 904

ThreadLocal 既然是线程级别,为什么线程结束,还会导致内存泄漏

ThreadLocal 虽然是线程级别的变量存储,但仍然可能导致内存泄漏,原因在于 ThreadLocal 的实现机制和引用关系。

C18298182575的博客 605

《为什么ThreadLocal会导致内存泄漏?一文还原问题现场》

ThreadLocal的内存泄漏问题是Java开发中常见且重要的话题。ThreadLocal为每个线程提供了独立的存储空间,类似于“私人储物柜”,但若不正确使用,可能导致内存泄漏。内存泄漏的根本原因是ThreadLocalMap中的Entry使用弱引用作为key,而value是强引用。当ThreadLocal实例被回收后,key会被GC回收,但value仍然存在,且无法被访问,导致内存无法释放,特别是在线程池等长生命周期线程中。 为避免内存泄漏,开发者应遵循以下最佳实践: 使用后必须调用remove():确

龚小帅的博客 861

ThreadLocal为什么会导致内存泄漏?如何解决?

为了解决这个问题,ThreadLocalMap使用了弱引用,这样ThreadLocal对象就可以在下次GC时被垃圾回收掉了,可以很大程度上避免因为ThreadLocal的使用导致的OOM问题,但是没法彻底避免。ThreadLocalMap的key就是ThreadLocal对象,他有两个引用源,一个是栈上的ThreadLocal引用,一个是ThreadLocalMap中的Key对他的引用。value只有一条引用,就是从Thread对象过来的。

摸鱼的快乐你不懂 305

ThreadLocal为什么会导致内存泄漏?如何解决的?

变量在使用线程池的场景下容易导致内存泄漏,因为线程不会结束生命周期。为了避免这种情况,应在使用完后显式调用remove()方法来清除存储的数据。

夏壹分享 472
上一篇: 浅谈延迟双删策略
下一篇: 面试官:导致Spring事务失效,常见的情况有哪些?
客官莫回头
博客等级 码龄6年 15粉丝 6原创
评论 2
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

个

红包个数最小为10个

元

红包金额最低5元

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

抵扣说明:

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

余额充值