Java Reference核心原理分析
{"type":"doc","content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"帶着問題,看源碼針對性會更強一點、印象會更深刻、並且效果也會更好。所以我先賣個關子,提兩個問題(沒準下次跳槽時就被問到)。"}]},{"type":"bulletedlist","content":[{"type":"listitem","content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"我們可以用ByteBuffer的allocateDirect方法,申請一塊堆外內存創建一個DirectByteBuffer對象,然後利用它去操作堆外內存。這些申請完的堆外內存,我們可以回收嗎?可以的話是通過什麼樣的機制回收的?"}]}]},{"type":"listitem","content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"大家應該都知道WeakHashMap可以用來實現內存相對敏感的本地緩存,爲什麼WeakHashMap合適這種業務場景,其內部實現會做什麼特殊處理呢?"}]}]}]},{"type":"heading","attrs":{"align":null,"level":1},"content":[{"type":"text","text":"GC可到達性與JDK中Reference類型"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"上面提到的兩個問題,其答案都在JDK的Reference裏面。JDK早期版本中並沒有Reference相關的類,這導致對象被GC回收後如果想做一些額外的清理工作(比如socket、堆外內存等)是無法實現的,同樣如果想要根據堆內存的實際使用情況決定要不要去清理一些內存敏感的對象也是法實現的。爲此JDK1.2中引入的Reference相關的類,即今天要介紹的Reference、SoftReference、WeakReference、PhantomReference,還有與之相關的Cleaner、ReferenceQueue、ReferenceHandler等。與Reference相關核心類基本都在java.lang.ref包下面。其類關係如下:"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"image","attrs":{"src":"https://static001.geekbang.org/infoq/4a/4ad1ea5f8488ba94c74f0c0deedbfaa4.png","alt":"reference.png","title":null,"style":null,"href":null,"fromPaste":true,"pastePass":true}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"其中,SoftReference代表軟引用對象,垃圾回收器會根據內存需求酌情回收軟引用指向的對象。普通的GC並不會回收軟引用,只有在即將OOM的時候(也就是最後一次Full GC)如果被引用的對象只有SoftReference指向的引用,纔會回收。WeakReference代表弱引用對象,當發生GC時,如果被引用的對象只有WeakReference指向的引用,就會被回收。PhantomReference代表虛引用對象(也有叫幻象引用的,個人認爲還是虛引用更加貼切),其是一種特殊的引用類型,不能通過虛引用獲取到其關聯的對象,但當GC時如果其引用的對象被回收,這個事件程序可以感知,這樣我們可以做相應的處理。最後就是最常見強引用對象,也就是通常我們new出來的對象。在繼續介紹Reference相關類的源碼前,先來簡單的看一下GC如何決定一個對象是否可被回收。其基本思路是從GC Root開始向下搜索,如果對象與GC Root之間存在引用鏈,則對象是可達的,GC會根據是否可到達與可到達性決定對象是否可以被回收。而對象的可達性與引用類型密切相關,對象的可到達性可分爲5種。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"強可到達,如果從GC Root搜索後,發現對象與GC Root之間存在強引用鏈則爲強可到達。強引用鏈即有強引用對象,引用了該對象。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"軟可到達,如果從GC Root搜索後,發現對象與GC Root之間不存在強引用鏈,但存在軟引用鏈,則爲軟可到達。軟引用鏈即有軟引用對象,引用了該對象。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"弱可到達,如果從GC Root搜索後,發現對象與GC Root之間不存在強引用鏈與軟引用鏈,但有弱引用鏈,則爲弱可到達。弱引用鏈即有弱引用對象,引用了該對象。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"虛可到達,如果從GC Root搜索後,發現對象與GC Root之間只存在虛引用鏈則爲虛可到達。虛引用鏈即有虛引用對象,引用了該對象。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"不可達,如果從GC Root搜索後,找不到對象與GC Root之間的引用鏈,則爲不可到達。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"看一個簡單的列子:"}]},{"type":"image","attrs":{"src":"https://static001.geekbang.org/infoq/8b/8b60c7e32ba011ba09aec69a613b0fe4.png","alt":"objectReach.png","title":null,"style":null,"href":null,"fromPaste":true,"pastePass":true}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"ObjectA爲強可到達,ObjectB也爲強可到達,雖然ObjectB對象被SoftReference ObjcetE 引用但由於其還被ObjectA引用所以爲強可到達;而ObjectC和ObjectD爲弱引用達到,雖然ObjectD對象被PhantomReference ObjcetG引用但由於其還被ObjectC引用,而ObjectC又爲弱引用達到,所以ObjectD爲弱引用達到;而ObjectH與ObjectI是不可到達。引用鏈的強弱有關係依次是 強引用 > 軟引用 > 弱引用 > 虛引用,如果有更強的引用關係存在,那麼引用鏈到達性,將由更強的引用有關係決定。"}]},{"type":"heading","attrs":{"align":null,"level":1},"content":[{"type":"text","text":"Reference核心處理流程"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"JVM在GC時如果當前對象只被Reference對象引用,JVM會根據Reference具體類型與堆內存的使用情況決定是否把對應的Reference對象加入到一個由Reference構成的pending鏈表上,如果能加入pending鏈表JVM同時會通知ReferenceHandler線程進行處理。ReferenceHandler線程是在Reference類被初始化時調用的,其是一個守護進程並且擁有最高的優先級。Reference類靜態初始化塊代碼如下:"}]},{"type":"codeblock","attrs":{"lang":null},"content":[{"type":"text","text":"static {\n //省略部分代碼...\n Thread handler = new ReferenceHandler(tg, \"Reference Handler\");\n handler.setPriority(Thread.MAX_PRIORITY);\n handler.setDaemon(true);\n handler.start();\n //省略部分代碼...\n}\n"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"而ReferenceHandler線程內部的run方法會不斷地從Reference構成的pending鏈表上獲取Reference對象,如果能獲取則根據Reference的具體類型進行不同的處理,不能則調用wait方法等待GC回收對象處理pending鏈表的通知。ReferenceHandler線程run方法源碼:"}]},{"type":"codeblock","attrs":{"lang":null},"content":[{"type":"text","text":"public void run() {\n //死循環,線程啓動後會一直運行\n while (true) {\n tryHandlePending(true);\n }\n}\n"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"run內部調用的tryHandlePending源碼:"}]},{"type":"codeblock","attrs":{"lang":null},"content":[{"type":"text","text":"static boolean tryHandlePending(boolean waitForNotify) {\n Reference
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.