http://blog.csdn.net/hudashi/article/details/7076880
ThreadLocal的目的就是為每一個使用ThreadLocal的線程都提供一個值,讓該值和使用它的線程綁定,當然每一個線程都可以獨立地改變它綁定的值。如果需要隔離多個線程之間的共享沖突,可以使用ThreadLocal,這將極大地簡化你的程序.
關于的ThreadLocal更多內容,請參考《ThreadLocal》。
在閱讀了ThreadLocal的源碼后,我發現如果我們使用不恰當,可能造成內存泄露。經我測試,內存泄露的確存在。雖然該內存泄露,理論上上已經不算嚴重。
測試代碼如下
ThreadLocalTest文件
~~~
package com.teleca.robin;
public class ThreadLocalTest {
public ThreadLocalTest()
{
}
ThreadLocal<Content> tl=new ThreadLocal<Content> ();
void start()
{
System.out.println("begin");
Content content=tl.get();
if(content==null)
{
content= new Content();
tl.set(content);
}
System.out.println("try to release content data");
//tl.set(null);//@1
//tl.remove();//@2
tl=null;//@3
content=null;//@4
System.out.println("request gc");
System.gc();
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
System.out.println("end");
}
}
class Content
{
byte data[]=new byte[1024*1024*10];
protected void finalize()
{
System.out.println("I am released");
}
}
~~~
運行結果
~~~
begin
try to release content data
request gc
end
~~~
注意我們嘗試在@3和@4處,釋放對tl和content的引用,以便JAVA虛擬機回收content。但是測試結果表明還有對content的引用,以致它沒有能被JAVA虛擬機回收。
我們必須把@1或@2處的代碼打開,才能把讓它讓JAVA虛擬機回收content.推薦打開@2而不是@1
把@1或@2處的代碼打開后的運行結果如下:
~~~
begin
try to release content data
request gc
I am released
end
~~~
另外注意,@3其實并不影響運行結果。
事實上每個Thread實例都有一個ThreadLocalMap成員變量,它以ThreadLocal對象為key,以ThreadLocal綁定的對象為Value。
.我們調用ThreadLocal的set()方法,只是把要綁定的對象存放在當前線程的ThreadLocalMap成員變量中,以便下次通過get()方法取得它。
ThreadLocalMap和普通map的最大區別就是它的Entry是針對ThreadLocal弱引用的,即當ThreadLocal沒有其他引用為空時,JVM就可以GC回收ThreadLocal,從而得到一個null的key。
關于ThreadLocalMap的更多內容請參考《為ThreadLocal定制的ThreadLocalMap》
ThreadlocalMap維護了ThreadLocal對象和其綁定對象之間的關系,這個ThreadLocalMap有threshold,當超過threshold時,
ThreadLocalMap會首先檢查內部ThreadLocal引用(前文說過,ThreadLocal是弱引用可以釋放)是否為null,如果存在null,那么把綁定對象的引用設置為null,以便釋放ThreadLocal綁定的對象,這樣就騰出了位置給新的ThreadLocal。如果不存在slate threadlocal,那么double threshold。
除此之外,還有兩個機會釋放掉已經廢棄的ThreadLocal綁定的對象所占用的內存,
一、當hash算法得到的table index剛好是一個null 的key的threadlocal時,直接用新的ThreadLocal替換掉已經廢棄的。
二、每次在ThreadLocalMap中存放ThreadLocal,hash算法沒有命中既有Entry,需要新建一個Entry時,也調用cleanSomeSlots來遍歷清理Entry數組中已經廢棄的ThreadLocal綁定的對象的引用。
此外,當Thread本身銷毀時,這個ThreadLocalMap也一定被銷毀了(ThreadLocalMap是Thread對象的成員),
這樣所有綁定到該線程的ThreadLocal的Object Value對象,如果在外部沒被引用的話(通常是這樣),也就沒有任何引用繼續保持,所以也就被銷毀回收了。
從上可以看出Java已經充分考慮了時間和空間的權衡,但是因為置為null的ThreadLocal對應的Object Value在無外部引用時,任然無法及時回收。
ThreadLocalMap只有到達threshold時或添加entry時才做檢查,不似gc是定時檢查,
不過我們可以手工通過ThreadLocal的remove()方法或set(null)解除ThreadLocalMap對ThreadLocal綁定對象的引用,及時的清理廢棄的threadlocal綁定對象的內存以。remove()往往還能做更多的清理工作,因此推薦使用它,而不使用set(null).
需要說明的是,只要不往不用的threadlocal中放入大量數據,問題不大,畢竟還有回收的機制。
被廢棄了的ThreadLocal所綁定對象的引用,會在以下4情況被清理。
如果此時外部沒有綁定對象的引用,則該綁定對象就能被回收了:
1 Thread結束時。
2 當Thread的ThreadLocalMap的threshold超過最大值時。
3 向Thread的ThreadLocalMap中存放一個ThreadLocal,hash算法沒有命中既有Entry,而需要新建一個Entry時。
4 手工通過ThreadLocal的remove()方法或set(null)。
因此如果我們粗暴的把ThreadLocal設置null,而不調用remove()方法或set(null),那么就可能造成ThreadLocal綁定的對象長期也能被回收,因而產出內存泄露。
- JVM
- 深入理解Java內存模型
- 深入理解Java內存模型(一)——基礎
- 深入理解Java內存模型(二)——重排序
- 深入理解Java內存模型(三)——順序一致性
- 深入理解Java內存模型(四)——volatile
- 深入理解Java內存模型(五)——鎖
- 深入理解Java內存模型(六)——final
- 深入理解Java內存模型(七)——總結
- Java內存模型
- Java內存模型2
- 堆內內存還是堆外內存?
- JVM內存配置詳解
- Java內存分配全面淺析
- 深入Java核心 Java內存分配原理精講
- jvm常量池
- JVM調優總結
- JVM調優總結(一)-- 一些概念
- JVM調優總結(二)-一些概念
- VM調優總結(三)-基本垃圾回收算法
- JVM調優總結(四)-垃圾回收面臨的問題
- JVM調優總結(五)-分代垃圾回收詳述1
- JVM調優總結(六)-分代垃圾回收詳述2
- JVM調優總結(七)-典型配置舉例1
- JVM調優總結(八)-典型配置舉例2
- JVM調優總結(九)-新一代的垃圾回收算法
- JVM調優總結(十)-調優方法
- 基礎
- Java 征途:行者的地圖
- Java程序員應該知道的10個面向對象理論
- Java泛型總結
- 序列化與反序列化
- 通過反編譯深入理解Java String及intern
- android 加固防止反編譯-重新打包
- volatile
- 正確使用 Volatile 變量
- 異常
- 深入理解java異常處理機制
- Java異常處理的10個最佳實踐
- Java異常處理手冊和最佳實踐
- Java提高篇——對象克隆(復制)
- Java中如何克隆集合——ArrayList和HashSet深拷貝
- Java中hashCode的作用
- Java提高篇之hashCode
- 常見正則表達式
- 類
- 理解java類加載器以及ClassLoader類
- 深入探討 Java 類加載器
- 類加載器的工作原理
- java反射
- 集合
- HashMap的工作原理
- ConcurrentHashMap之實現細節
- java.util.concurrent 之ConcurrentHashMap 源碼分析
- HashMap的實現原理和底層數據結構
- 線程
- 關于Java并發編程的總結和思考
- 40個Java多線程問題總結
- Java中的多線程你只要看這一篇就夠了
- Java多線程干貨系列(1):Java多線程基礎
- Java非阻塞算法簡介
- Java并發的四種風味:Thread、Executor、ForkJoin和Actor
- Java中不同的并發實現的性能比較
- JAVA CAS原理深度分析
- 多個線程之間共享數據的方式
- Java并發編程
- Java并發編程(1):可重入內置鎖
- Java并發編程(2):線程中斷(含代碼)
- Java并發編程(3):線程掛起、恢復與終止的正確方法(含代碼)
- Java并發編程(4):守護線程與線程阻塞的四種情況
- Java并發編程(5):volatile變量修飾符—意料之外的問題(含代碼)
- Java并發編程(6):Runnable和Thread實現多線程的區別(含代碼)
- Java并發編程(7):使用synchronized獲取互斥鎖的幾點說明
- Java并發編程(8):多線程環境中安全使用集合API(含代碼)
- Java并發編程(9):死鎖(含代碼)
- Java并發編程(10):使用wait/notify/notifyAll實現線程間通信的幾點重要說明
- java并發編程-II
- Java多線程基礎:進程和線程之由來
- Java并發編程:如何創建線程?
- Java并發編程:Thread類的使用
- Java并發編程:synchronized
- Java并發編程:Lock
- Java并發編程:volatile關鍵字解析
- Java并發編程:深入剖析ThreadLocal
- Java并發編程:CountDownLatch、CyclicBarrier和Semaphore
- Java并發編程:線程間協作的兩種方式:wait、notify、notifyAll和Condition
- Synchronized與Lock
- JVM底層又是如何實現synchronized的
- Java synchronized詳解
- synchronized 與 Lock 的那點事
- 深入研究 Java Synchronize 和 Lock 的區別與用法
- JAVA編程中的鎖機制詳解
- Java中的鎖
- TreadLocal
- 深入JDK源碼之ThreadLocal類
- 聊一聊ThreadLocal
- ThreadLocal
- ThreadLocal的內存泄露
- 多線程設計模式
- Java多線程編程中Future模式的詳解
- 原子操作(CAS)
- [譯]Java中Wait、Sleep和Yield方法的區別
- 線程池
- 如何合理地估算線程池大小?
- JAVA線程池中隊列與池大小的關系
- Java四種線程池的使用
- 深入理解Java之線程池
- java并發編程III
- Java 8并發工具包漫游指南
- 聊聊并發
- 聊聊并發(一)——深入分析Volatile的實現原理
- 聊聊并發(二)——Java SE1.6中的Synchronized
- 文件
- 網絡
- index
- 內存文章索引
- 基礎文章索引
- 線程文章索引
- 網絡文章索引
- IOC
- 設計模式文章索引
- 面試
- Java常量池詳解之一道比較蛋疼的面試題
- 近5年133個Java面試問題列表
- Java工程師成神之路
- Java字符串問題Top10
- 設計模式
- Java:單例模式的七種寫法
- Java 利用枚舉實現單例模式
- 常用jar
- HttpClient和HtmlUnit的比較總結
- IO
- NIO
- NIO入門
- 注解
- Java Annotation認知(包括框架圖、詳細介紹、示例說明)