當前位置:網站首頁>ThreadLocal使用不好,小心造成內存泄露!

ThreadLocal使用不好,小心造成內存泄露!

2022-01-26 23:24:58 程序員社區

一、前言

對ThreadLocal不熟悉的同學,可以先參考我的另外一篇文章淺談ThreadLocal

在討論內存泄漏之前,需要明白java中的四種引用,同樣可以移步到java中的四種引用

什麼是內存泄露?

大白話講,就是我自己創建的對象,在一系列操作後,我訪問不到該對象了,我認為它已經被回收掉了,但該對象卻一直存在與內存中。


二、示例

先給出一個簡單例子,用來說明引用與對象的指向關系

package com.yang.testThreadLocal;public class Main {    private static ThreadLocal<Integer> tl = new ThreadLocal<>();    public static void main(String[] args) {        tl.set(1);        Thread t = new Thread(() -> {            tl.set(2);            System.out.println("子線程:" + tl.get());        });        t.start();        System.out.println("主線程:" + tl.get());    }}

指向關系如下所示,主線程就不畫了,各比特別嫌弃圖的配色,我真的是配不出來了。

ThreadLocal使用不好,小心造成內存泄露!插圖

在ThreadLocalMap中,維護一個Entry類型的數組,Entry是一個(k,v)結構,可以把ThreadLocalMap理解為一個定制化的HashMap。不過Entry的key是對ThreadLocal的一個弱引用,在執行tl=null後,1號線斷開,則該ThreadLocal會在下一次GC到來的時候,被回收掉。

為什麼這裏的key保持著對ThreadLocal的一個弱引用呢?保持强引用行不行?

假設這裏的key保持對ThreadLocal的强引用,則當我的程序用不到該ThreadLocal時,我手動執行了tl=null,此時1號線斷開,而這裏的5號線是實線,5號線沒有斷開,因此ThreadLocal對象無法被回收掉,一直存在於內存中,造成內存泄露。

看來,這裏的弱引用,能够保證用不到的ThreadLocal被回收掉。

弱引用就能完全防止內存泄露了嗎?

由上面的分析,弱引用能够防止釋放不掉ThreadLocal引起的內存泄露。但是,卻不能防止釋放不掉Integer引起的內存泄露。首先,執行tl=null,則1號線斷開,GC到來時,5號線斷開,此時ThreadLocal被回收掉了,這個key被置為了null,可是這個key對應的value强引用著Integer對象,該Integer無法在用戶代碼中訪問到了,但卻依然存在於內存中,造成內存泄露。

既然依然存在著內存泄露,那麼JDK團隊是怎麼解决的呢?

其實,ThreadLocal中的get()、set()方法,不是單純地去做獲取、設置的操作。在它們的方法內部,依然會遍曆該Entry數組,删除所有key為null的Entry,並將相關的value置為null,從而够解决因釋放不掉value而引起的內存泄露。

有這些get()、set()方法,就能完全地防止內存泄漏嗎?

但我們手動將tl置為null後,就已經沒法調用這些get()、set()方法了。所以,預防內存泄露的最佳實踐是,在使用完ThreadLocal後,先調用tl.remove(),再調用tl=null。tl.remove()能够使得ThreadLocalMap删除該ThreadLocal所在的Entry,以及將value置為null,tl=null使得ThreadLocal對象真正地被回收掉。

 

版權聲明
本文為[程序員社區]所創,轉載請帶上原文鏈接,感謝
https://cht.chowdera.com/2022/01/202201262324580232.html

隨機推薦