欧美bbbwbbbw肥妇,免费乱码人妻系列日韩,一级黄片

關于ThreadLocal使用時OOM的討論

 更新時間:2025年06月29日 13:40:01   作者:找不到、了  
這篇文章主要介紹了關于ThreadLocal使用時OOM的討論,具有很好的參考價值,希望對大家有所幫助,如有錯誤或未考慮完全的地方,望不吝賜教

之前介紹Spring bean線程安全的問題時候,討論到 ThreadLocal 類提供了線程局部變量,每個線程可以將一個值存在 ThreadLocal 對象中,其他線程無法訪問這些值。每個線程都有自己獨立的變量副本。

但如果使用不當,它可能會導致 內存泄漏(Memory Leak),最終引發(fā) (OOM)。根本原因在于 ThreadLocal 的存儲機制 和 垃圾回收(GC)行為。

1、數(shù)據(jù)結構

位于java.lang包下面。

1.1、內存存儲結構

ThreadLocal 的核心存儲依賴于:

  • ThreadLocalMap(每個 Thread 內部維護的一個類似 WeakHashMap 的結構)
  • EntryThreadLocalMap 的存儲單元,key 是 ThreadLocal 本身,value 是存儲的值)

如下圖所示:

定義時候,可參考如下:

ThreadLocal.ThreadLocalMap threadLocals; // 每個線程的 ThreadLocal 數(shù)據(jù)存儲在這里

ThreadLocalMap 的 Entry 是 弱引用(WeakReference) 的:

static class Entry extends WeakReference<ThreadLocal<?>> {
    Object value; // 存儲的值是強引用
    Entry(ThreadLocal<?> k, Object v) {
        super(k); // key(ThreadLocal)是弱引用
        value = v; // value 是強引用
    }
}

而對于key是弱引用,value是強引用。

2. 內存泄漏

如下圖所示:

        ThreadLocal 的內存泄漏問題主要發(fā)生在 線程池環(huán)境(如 Tomcat、Spring 的異步任務等),因為線程會被復用,導致 ThreadLocalMap 長期存活。

2.1、引用回收

key(ThreadLocal)是弱引用

  • 如果 ThreadLocal 對象沒有外部強引用(比如 static 修飾),它會被 GC 回收,Entry 的 key 變成 null。

value 是強引用

  • 即使 key 被回收,value 仍然被 ThreadLocalMap 強引用,無法被 GC 回收。

2.2、value的強引用目的

1、如果是弱引用,調用get方法,返回為null,value 可能被提前回收,導致數(shù)據(jù)丟失。

2、設計目標是 讓每個線程可以安全地存儲自己的數(shù)據(jù),而不是讓數(shù)據(jù)隨時可能被回收。如果 value 是弱引用,就失去了存儲數(shù)據(jù)的可靠性。

2.3、線程長期存活

如果線程是線程池中的(如 Tomcat 的工作線程),線程不會銷毀,ThreadLocalMap 會一直存在。

如果 ThreadLocal 使用后沒有 remove(),value 會一直占用內存,最終導致 內存泄漏

示例如下:

public class UserContextHolder {
    private static ThreadLocal<User> userHolder = new ThreadLocal<>();

    public static void set(User user) {
        userHolder.set(user);
    }

    public static User get() {
        return userHolder.get();
    }
    
    // 忘記調用 remove()!
}

問題

  • 每次 HTTP 請求結束后,Tomcat 線程不會銷毀,而是放回線程池。
  • 如果 User 對象很大,多次請求后,ThreadLocalMap 會積累大量 User 對象,最終 OOM。

小結

3、處理方案

先根據(jù)數(shù)據(jù)結構進行分析,如下圖所示:

3.1、remove

try {
    UserContextHolder.set(user);
    // ...業(yè)務邏輯
} finally {
    UserContextHolder.remove(); // 必須清理!
}

最佳實踐:在 finally 塊中調用 remove(),確保即使發(fā)生異常也能清理。

3.2、static修飾

private static final ThreadLocal<User> userHolder = new ThreadLocal<>();

原因:防止 ThreadLocal 被意外回收(弱引用失效)。

3.3、避免存儲大對象

如果 ThreadLocal 存儲的是大對象(如緩存、Session 數(shù)據(jù)),考慮改用其他方式(如 Redis)。

3.4、InheritableThreadLocal

InheritableThreadLocal 會傳遞給子線程,如果子線程不清理,同樣會導致內存泄漏。

小結

總結

以上為個人經(jīng)驗,希望能給大家一個參考,也希望大家多多支持腳本之家。

相關文章

  • java組件commons-fileupload實現(xiàn)文件上傳、下載、在線打開

    java組件commons-fileupload實現(xiàn)文件上傳、下載、在線打開

    這篇文章主要介紹了java組件commons-fileupload實現(xiàn)文件上傳、下載、在線打開,具有一定的參考價值,感興趣的小伙伴們可以參考一下
    2016-10-10
  • Java之SpringCloud nocos注冊中心講解

    Java之SpringCloud nocos注冊中心講解

    這篇文章主要介紹了Java之SpringCloud nocos注冊中心講解,本篇文章通過簡要的案例,講解了該項技術的了解與使用,以下就是詳細內容,需要的朋友可以參考下
    2021-08-08
  • 詳解領域驅動設計之事件驅動與CQRS

    詳解領域驅動設計之事件驅動與CQRS

    這篇文章分析了如何應用事件來分離軟件核心復雜度。探究CQRS為什么廣泛應用于DDD項目中,以及如何落地實現(xiàn)CQRS框架。當然我們也要警惕一些失敗的教訓,利弊分析以后再去抉擇正確的應對之道
    2021-06-06
  • idea導入項目爆紅問題記錄以及解決

    idea導入項目爆紅問題記錄以及解決

    這篇文章主要介紹了idea導入項目爆紅問題記錄以及解決方案,具有很好的參考價值,希望對大家有所幫助。如有錯誤或未考慮完全的地方,望不吝賜教
    2023-07-07
  • SpringBoot項目jar和war打包部署方式詳解

    SpringBoot項目jar和war打包部署方式詳解

    這篇文章主要為大家介紹了SpringBoot項目jar和war打包部署方式詳解,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進步,早日升職加薪
    2022-09-09
  • ThreadLocal數(shù)據(jù)存儲結構原理解析

    ThreadLocal數(shù)據(jù)存儲結構原理解析

    這篇文章主要為大家介紹了ThreadLocal數(shù)據(jù)存儲結構原理解析,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進步,早日升職加薪
    2022-10-10
  • Java中泛型使用實例詳解

    Java中泛型使用實例詳解

    這篇文章主要介紹了Java中泛型使用實例詳解的相關資料,需要的朋友可以參考下
    2017-05-05
  • java高級應用:線程池的全面講解(干貨)

    java高級應用:線程池的全面講解(干貨)

    這篇文章主要介紹了java高級應用:線程池的全面講解(干貨),具有很好的參考價值,希望對大家有所幫助。一起跟隨小編過來看看吧
    2021-02-02
  • 基于zipoutputStream的簡單使用

    基于zipoutputStream的簡單使用

    這篇文章主要介紹了基于zipoutputStream的簡單使用方式,具有很好的參考價值,希望對大家有所幫助,如有錯誤或未考慮完全的地方,望不吝賜教
    2023-12-12
  • java 多線程Thread與runnable的區(qū)別

    java 多線程Thread與runnable的區(qū)別

    這篇文章主要介紹了java 多線程Thread與runnable的區(qū)別的相關資料,java線程有兩種方法繼承thread類與實現(xiàn)runnable接口,下面就提供實例幫助大家理解,需要的朋友可以參考下
    2017-08-08

最新評論