Java四種引用類型:強引用、軟引用、弱引用、虛引用

{"type":"doc","content":[{"type":"heading","attrs":{"align":null,"level":1},"content":[{"type":"text","text":"Java爲什麼要設計這四種引用"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"Java的內存分配和內存回收,都不需要程序員負責,都是由偉大的JVM去負責,一個對象是否可以被回收,主要看是否有引用指向此對象,說的專業點,叫可達性分析。"}]},{"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":"Java設計這四種引用的主要目的有兩個:"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"numberedlist","attrs":{"start":"1","normalizeStart":1},"content":[{"type":"listitem","content":[{"type":"paragraph","attrs":{"indent":0,"number":1,"align":null,"origin":null},"content":[{"type":"text","text":"可以讓程序員通過代碼的方式來決定某個對象的生命週期;"}]}]},{"type":"listitem","content":[{"type":"paragraph","attrs":{"indent":0,"number":2,"align":null,"origin":null},"content":[{"type":"text","text":"有利用垃圾回收。"}]}]}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"heading","attrs":{"align":null,"level":1},"content":[{"type":"text","text":"強引用"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"強引用是最普遍的一種引用,我們寫的代碼,99.9999%都是強引用:"}]},{"type":"codeblock","attrs":{"lang":"java"},"content":[{"type":"text","text":"Object o = new Object();"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"這種就是強引用了,是不是在代碼中隨處可見,最親切。"}]},{"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":"只要某個對象有強引用與之關聯,這個對象永遠不會被回收,即使內存不足,JVM寧願拋出OOM,也不會去回收。"}]},{"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":"那麼什麼時候纔可以被回收呢?當強引用和對象之間的關聯被中斷了,就可以被回收了。"}]},{"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":"我們可以手動把關聯給中斷了,方法也特別簡單:"}]},{"type":"codeblock","attrs":{"lang":"java"},"content":[{"type":"text","text":"o = null;"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"我們可以手動調用GC,看看如果強引用和對象之間的關聯被中斷了,資源會不會被回收,爲了更方便、更清楚的觀察到回收的情況,我們需要新寫一個類,然後重寫finalize方法,下面我們來進行這個實驗:"}]},{"type":"codeblock","attrs":{"lang":"java"},"content":[{"type":"text","text":"public class Student { @Override\n protected void finalize() throws Throwable {\n System.out.println(\"Student 被回收了\");\n }}public static void main(String[] args) {\n Student student = new Student();\n student = null;\n System.gc();}"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"運行結果:"}]},{"type":"codeblock","attrs":{"lang":"java"},"content":[{"type":"text","text":"Student 被回收了"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"可以很清楚的看到資源被回收了。"}]},{"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","marks":[{"type":"italic"},{"type":"strong"}],"text":"當然,在實際開發中,千萬不要重寫finalize方法"}]},{"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":"在實際的開發中,看到有一些對象被手動賦值爲NULL,很大可能就是爲了“特意提醒”JVM這塊資源可以進行垃圾回收了。點擊這裏獲取一份 JVM 實戰教程。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"heading","attrs":{"align":null,"level":1},"content":[{"type":"text","text":"軟引用"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"下面先來看看如何創建一個軟引用:"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"codeblock","attrs":{"lang":"java"},"content":[{"type":"text","text":" SoftReferencestudentSoftReference=new SoftReference(new Student());"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"軟引用就是把對象用SoftReference包裹一下,當我們需要從軟引用對象獲得包裹的對象,只要get一下就可以了:"}]},{"type":"codeblock","attrs":{"lang":"java"},"content":[{"type":"text","text":"SoftReferencestudentSoftReference=new SoftReference(new Student());\nStudent student = studentSoftReference.get();\nSystem.out.println(student);"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","marks":[{"type":"strong"}],"text":"軟引用有什麼特點呢:"}]},{"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":"當內存不足,會觸發JVM的GC,如果GC後,內存還是不足,就會把軟引用的包裹的對象給幹掉,也就是隻有在內存不足,JVM纔會回收該對象。"}]},{"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":"還是一樣的,必須做實驗,才能加深印象:"}]},{"type":"codeblock","attrs":{"lang":"java"},"content":[{"type":"text","text":"SoftReference softReference = new SoftReference(new byte[1024*1024*10]);\nSystem.out.println(softReference.get());\nSystem.gc();System.out.println(softReference.get()); \nbyte[] bytes = new byte[1024 * 1024 * 10];\nSystem.out.println(softReference.get());"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"我定義了一個軟引用對象,裏面包裹了byte[],byte[]佔用了10M,然後又創建了10Mbyte[]。"}]},{"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":"運行程序,需要帶上一個參數:"}]},{"type":"codeblock","attrs":{"lang":"java"},"content":[{"type":"text","text":"-Xmx20M"}]},{"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":"代表最大堆內存是20M。"}]},{"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":"運行結果:"}]},{"type":"codeblock","attrs":{"lang":"java"},"content":[{"type":"text","text":"[B@11d7fff\n[B@11d7fff\nnull"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"可以很清楚的看到手動完成GC後,軟引用對象包裹的byte[]還活的好好的,但是當我們創建了一個10M的byte[]後,最大堆內存不夠了,所以把軟引用對象包裹的byte[]給幹掉了,如果不幹掉,就會拋出OOM。"}]},{"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":"軟引用到底有什麼用呢?比較適合用作緩存,當內存足夠,可以正常的拿到緩存,當內存不夠,就會先幹掉緩存,不至於馬上拋出OOM。說到緩存,大家可以關注微信公衆號Java技術棧獲取更多幹貨。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"heading","attrs":{"align":null,"level":1},"content":[{"type":"text","text":"弱引用"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"弱引用的使用和軟引用類似,只是關鍵字變成了WeakReference:"}]},{"type":"codeblock","attrs":{"lang":"java"},"content":[{"type":"text","text":"WeakReference weakReference = new WeakReference(new byte[1024\\*1024\\*10]);\nSystem.out.println(weakReference.get());"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"弱引用的特點是不管內存是否足夠,只要發生GC,都會被回收:"}]},{"type":"codeblock","attrs":{"lang":"java"},"content":[{"type":"text","text":"WeakReference weakReference = new WeakReference(new byte[1]);\nSystem.out.println(weakReference.get());\nSystem.gc();System.out.println(weakReference.get());"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"運行結果:"}]},{"type":"codeblock","attrs":{"lang":"java"},"content":[{"type":"text","text":"[B@11d7fffnull"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"可以很清楚的看到明明內存還很充足,但是觸發了GC,資源還是被回收了。 "}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"弱引用在很多地方都有用到,比如ThreadLocal、WeakHashMap。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"heading","attrs":{"align":null,"level":1},"content":[{"type":"text","text":"虛引用"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"虛引用又被稱爲幻影引用,我們來看看它的使用:"}]},{"type":"codeblock","attrs":{"lang":"java"},"content":[{"type":"text","text":"ReferenceQueue queue = new ReferenceQueue();\nPhantomReference reference = new PhantomReference(new byte[1], queue);\nSystem.out.println(reference.get());"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"虛引用的使用和上面說的軟引用、弱引用的區別還是挺大的,我們先不管ReferenceQueue 是個什麼鬼,直接來運行:"}]},{"type":"codeblock","attrs":{"lang":"java"},"content":[{"type":"text","text":"null"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"竟然打印出了null,我們來看看get方法的源碼:"}]},{"type":"codeblock","attrs":{"lang":"java"},"content":[{"type":"text","text":"public T get() { \n  return null;\n}"}]},{"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":"這是幾個意思,竟然直接返回了null。"}]},{"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":"這就是虛引用特點之一了:無法通過虛引用來獲取對一個對象的真實引用。"}]},{"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":"那虛引用存在的意義是什麼呢?這就要回到我們上面的代碼了,我們把代碼複製下,以免大家再次往上翻:"}]},{"type":"codeblock","attrs":{"lang":"java"},"content":[{"type":"text","text":"ReferenceQueue queue = new ReferenceQueue();\nPhantomReference reference = new PhantomReference(new byte[1], queue);\nSystem.out.println(reference.get());"}]},{"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":"創建虛引用對象,我們除了把包裹的對象傳了進去,還傳了一個ReferenceQueue,從名字就可以看出它是一個隊列。"}]},{"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":"虛引用的特點之二就是 虛引用必須與ReferenceQueue一起使用,當GC準備回收一個對象,如果發現它還有虛引用,就會在回收之前,把這個虛引用加入到與之關聯的ReferenceQueue中。"}]},{"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":"我們來用代碼實踐下吧:"}]},{"type":"codeblock","attrs":{"lang":"java"},"content":[{"type":"text","text":"ReferenceQueue queue = new ReferenceQueue();\nList bytes = new ArrayList<>();\nPhantomReference reference = new PhantomReference(new Student(),queue);\nnew Thread(() -> {\n for (int i = 0; i < 100;i++ ) {\n bytes.add(new byte[1024 * 1024]);\n }}).start();new Thread(() -> {\n while (true) {\n Reference poll = queue.poll(); if (poll != null) {\n System.out.println(\"虛引用被回收了:\" + poll);\n } }}).start();Scanner scanner = new Scanner(System.in);\nscanner.hasNext();}"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"運行結果:"}]},{"type":"codeblock","attrs":{"lang":"java"},"content":[{"type":"text","text":"Student 被回收了\n虛引用被回收了:java.lang.ref.PhantomReference@1ade6f1"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"我們簡單的分析下代碼: "}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"第一個線程往集合裏面塞數據,隨着數據越來越多,肯定會發生GC。 "}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"第二個線程死循環,從queue裏面拿數據,如果拿出來的數據不是null,就打印出來。"}]},{"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":"從運行結果可以看到:當發生GC,虛引用就會被回收,並且會把回收的通知放到ReferenceQueue中。"}]},{"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":"虛引用有什麼用呢?在NIO中,就運用了虛引用管理堆外內存."}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}}]}
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章