最新国产好看的视频,伊人天堂AV在线,国产Aaaaaa视频,蜜臀视频在线观看一区,人妻av色图,密臀久久久精品影片,青青视频免费观看毛片,久草在线观看视,国产三级精品色情在线

ThreadLocal導(dǎo)致JVM內(nèi)存泄漏原因探究

 更新時間:2023年04月19日 11:24:57   作者:胡尚  
ThreadLocal是JDK提供的線程本地變量機制,但若使用不當(dāng)可能導(dǎo)致內(nèi)存泄漏。正確的使用方式是在使用完后及時remove,或者使用弱引用等手段避免強引用導(dǎo)致的內(nèi)存泄漏。在多線程編程中,合理使用ThreadLocal可以提高并發(fā)性能,但也需要注意其潛在的內(nèi)存泄漏問題

為什么要使用ThreadLocal

在一整個業(yè)務(wù)邏輯流程中,為了在不同的地方或者不同的方法中使用同一個對象,但是又不想在方法形參中加這個對象,那么就可以使用ThreadLocal來保存

ThreadLocal最大的應(yīng)用場景就是跨方法進(jìn)行參數(shù)傳遞

ThreadLocal可以給每一個線程綁定一個變量的副本

使用ThreadLocal

ThreadLocal常用的方法其實也就下面幾個

// 返回當(dāng)前線程所對應(yīng)的線程局部變量。
public T get() {}
// 設(shè)置當(dāng)前線程的線程局部變量的值。
public void set(T value) {}
// 移除,當(dāng)線程結(jié)束后,該線程thread對象中的局部變量將在下一次gc時回收,如果顯示的調(diào)用此方法只是可以加快內(nèi)存回收的速度
// 所以javase開發(fā) 普通new Thread()方式中,這個方法并不是必須要調(diào)用的
// 但是javaWeb開發(fā)中就必須顯示調(diào)用,因為javaweb都是使用的線程池,并不是一個客戶端來一個請求,thread線程對象用完就刪除,而是會放回線程池中。
public void remove() {}
// 返回該線程局部變量的一個初始化
// protected方法,顯然是為了讓子類覆蓋而設(shè)計的。這個方法在第一次調(diào)用 get()或 set(Object)時才執(zhí)行,并且僅執(zhí)行 1 次
protected T initialValue() {}

在具體使用的時候,我們ThreadLocal對象一定會定義成靜態(tài)的,如果不定義成靜態(tài)的那么其他地方如何通過這個ThreadLocal實例去Map中拿數(shù)據(jù)嘞?

而且如果是多個線程保存一個變量的副本,一個靜態(tài)的ThreadLocal也足夠了,因為它是作為多個map中的key存在的

簡單使用案例

/**
 * @Description: 在一個方法中調(diào)用set()方法存值,在另一個方法中調(diào)用get()方法取值
 */
public class UseThreadLocalTest {
    public static ThreadLocal<String> threadLocal = new ThreadLocal<>();
    /**
     * 創(chuàng)建一個線程類
     */
    public static class ThreadTest extends Thread{
        private Integer id;
        ThreadTest(Integer id){
            this.id = id;
        }
        @Override
        public void run() {
            threadLocal.set(Thread.currentThread().getName() + ":" + id);
            print();
        }
        public void print(){
            System.out.println(threadLocal.get());
        }
    }
    /**
     * 開三個線程
     */
    public static void main(String[] args) {
        for (int i = 0; i < 3; i++) {
            new ThreadTest(i).start();
        }
    }
}

// 輸入結(jié)果如下
Thread-0:0
Thread-1:1
Thread-2:2

具體實現(xiàn)

ThreadLocal底層set()和get()方法的源碼如下

// 存值時 map最終是存儲在當(dāng)前線程Thread t = Thread.currentThread()中的,是thread的一個成員變量
// map的key是當(dāng)前threadLocal對象實例,value是要存的值
public void set(T value) {
    Thread t = Thread.currentThread();
    ThreadLocalMap map = getMap(t);
    if (map != null)
        map.set(this, value);
    else
        createMap(t, value);
}
// 取值時也是也是先從當(dāng)前線程Thread對象中取出map
// 然后在從map中根據(jù)當(dāng)前threadLocal對象實例作為key獲取到entry對象
public T get() {
    Thread t = Thread.currentThread();
    ThreadLocalMap map = getMap(t);
    if (map != null) {
        ThreadLocalMap.Entry e = map.getEntry(this);
        if (e != null) {
            @SuppressWarnings("unchecked")
            T result = (T)e.value;
            return result;
        }
    }
    return setInitialValue();
}

為了提高性能,才沒有采用加鎖的方式,而是將map和各個線程thread對象進(jìn)行關(guān)聯(lián),這樣就避免了產(chǎn)生線程安全問題,也避免了加鎖,提高了性能

我們接下來再來看看ThreadLocalMap它的實現(xiàn),它類似于jdk1.7版本的hashmap,底層存儲的是一個Entry對象的數(shù)組,初始容量也是16,存值時先用hash結(jié)果和數(shù)組長度取余得到數(shù)組下標(biāo)位置,然后判斷是否產(chǎn)生了hash沖突,然后使用開發(fā)定址法來處理。根據(jù)算法的不同又可以分為線性探測再散列、二次探測再散列、偽隨機探測再散列。ThreadLocalMap它是使用的線性探測再散列法,如下所示

private static int nextIndex(int i, int len) {
    return ((i + 1 < len) ? i + 1 : 0);
}

Entry對象中的key它是一個弱引用,Entry繼承了WeakReference類,弱引用跟沒引用差不多,GC會直接回收掉,不管內(nèi)存是否足夠都會回收

static class Entry extends WeakReference<ThreadLocal<?>> {
    Object value;
    Entry(ThreadLocal<?> k, Object v) {
        super(k);
        value = v;
    }
}

引發(fā)內(nèi)存泄漏的原因

上面再介紹ThreadLocal基本使用api方法的時候也提到了,如果只是創(chuàng)建一個普通的線程Thread對象,是不會產(chǎn)生內(nèi)存泄漏問題的。因為map是存儲在Thread對象中,一個普通線程執(zhí)行完了,那么這個線程的局部變量也就會被gc回收。

但如果結(jié)合到了線程池,一個Thread線程對象用完后放回線程池中,如果這個時候我們程序不顯示的調(diào)用remove()方法,那么就會造成內(nèi)存泄漏問題了。

因為Entry對象中的Key的弱引用,但是value還會存在,就會存在map中key為null的value

ThreadLocal 的底層實現(xiàn)中我們可以看見,無論是 get()、set()在某些時 候,調(diào)用了 expungeStaleEntry() 方法用來清除 Entry 中 Key 為 null 的 Value,但是這是不及時的,也不是每次都會執(zhí)行的,所以一些情況下還是會發(fā)生內(nèi)存泄露。

到此這篇關(guān)于ThreadLocal導(dǎo)致JVM內(nèi)存泄漏原因探究的文章就介紹到這了,更多相關(guān)JVM內(nèi)存泄漏內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!

相關(guān)文章

  • Spring Cloud如何切換Ribbon負(fù)載均衡模式

    Spring Cloud如何切換Ribbon負(fù)載均衡模式

    這篇文章主要介紹了Spring Cloud如何切換Ribbon負(fù)載均衡模式,文中通過示例代碼介紹的非常詳細(xì),對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價值,需要的朋友可以參考下
    2019-12-12
  • 深入理解Java設(shè)計模式之模板方法模式

    深入理解Java設(shè)計模式之模板方法模式

    這篇文章主要介紹了JAVA設(shè)計模式之模板方法模式的的相關(guān)資料,文中示例代碼非常詳細(xì),供大家參考和學(xué)習(xí),感興趣的朋友可以了解
    2021-11-11
  • java ReentrantLock條件鎖實現(xiàn)原理示例詳解

    java ReentrantLock條件鎖實現(xiàn)原理示例詳解

    這篇文章主要為大家介紹了java ReentrantLock條件鎖實現(xiàn)原理示例詳解,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪
    2023-01-01
  • Mybatis plus的自動填充與樂觀鎖的實例詳解(springboot)

    Mybatis plus的自動填充與樂觀鎖的實例詳解(springboot)

    這篇文章主要介紹了Mybatis plus的自動填充與樂觀鎖的實例詳解(springboot),本文給大家介紹的非常詳細(xì)對大家的學(xué)習(xí)或工作具有一定的參考借鑒價值,需要的朋友可以參考下
    2020-11-11
  • Java 語言實現(xiàn)清除帶 html 標(biāo)簽的內(nèi)容方法

    Java 語言實現(xiàn)清除帶 html 標(biāo)簽的內(nèi)容方法

    下面小編就為大家?guī)硪黄狫ava 語言實現(xiàn)清除帶 html 標(biāo)簽的內(nèi)容方法。小編覺得挺不錯的,現(xiàn)在就分享給大家,也給大家做個參考。一起跟隨小編過來看看吧
    2017-02-02
  • 在SpringBoot中使用UniHttp簡化天地圖路徑規(guī)劃調(diào)用實踐記錄(場景分析)

    在SpringBoot中使用UniHttp簡化天地圖路徑規(guī)劃調(diào)用實踐記錄(場景分析)

    本文介紹了如何在SpringBoot項目中使用UniHttp簡化天地圖路徑規(guī)劃接口的調(diào)用,通過一個具體的例子展示了如何根據(jù)中文地址獲取經(jīng)緯度坐標(biāo),并使用UniHttp調(diào)用天地圖路徑規(guī)劃服務(wù),感興趣的朋友一起看看吧
    2025-02-02
  • Springboot集成magic-api的詳細(xì)過程

    Springboot集成magic-api的詳細(xì)過程

    這篇文章主要介紹了Springboot集成magic-api的相關(guān)知識,本文結(jié)合實例代碼給大家介紹的非常詳細(xì),對大家的學(xué)習(xí)或工作具有一定的參考借鑒價值,需要的朋友可以參考下
    2023-06-06
  • BaseMapper接口的使用方法

    BaseMapper接口的使用方法

    這篇文章主要介紹了BaseMapper接口的使用方法,本文通過示例代碼給大家介紹的非常詳細(xì),對大家的學(xué)習(xí)或工作具有一定的參考借鑒價值,需要的朋友參考下吧
    2023-12-12
  • java8新特性 獲取list某一列的操作

    java8新特性 獲取list某一列的操作

    這篇文章主要介紹了java8新特性 獲取list某一列的操作,具有很好的參考價值,希望對大家有所幫助。一起跟隨小編過來看看吧
    2021-02-02
  • Spring security用戶URL權(quán)限FilterSecurityInterceptor使用解析

    Spring security用戶URL權(quán)限FilterSecurityInterceptor使用解析

    這篇文章主要介紹了Spring security用戶URL權(quán)限FilterSecurityInterceptor使用解析,文中通過示例代碼介紹的非常詳細(xì),對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價值,需要的朋友可以參考下
    2019-12-12

最新評論

山丹县| 抚顺县| 集安市| 高碑店市| 绥滨县| 龙泉市| 吴川市| 淮南市| 晋州市| 佳木斯市| 石柱| 聊城市| 康定县| 丽江市| 新营市| 平湖市| 西峡县| 团风县| 卫辉市| 阳西县| 金秀| 高清| 嘉荫县| 惠安县| 宜州市| 神木县| 陇西县| 鄂州市| 牡丹江市| 罗甸县| 麦盖提县| 安平县| 佛山市| 绥滨县| 确山县| 通河县| 商南县| 大邑县| 务川| 宁都县| 平泉县|