Java程序员的日常—— 垃圾回收中引用类型的作用
在Java里面,是不需要太過于關乎垃圾回收,但是這并不意味著開發者可以不了解垃圾回收的機制,況且在java中內存泄露也是家常便飯的事情。因此了解垃圾回收的相關知識就顯得很重要了。
引用,在垃圾回收中是一個很關鍵的概念,它關系到如何辨別這個對象是否被回收,什么時機回收。
引用的類型
在Java中引用的類型可以分為四個類型,依次是:
- 強引用:在任何時間JVM都不會進行回收
- 軟引用:在內存不夠的時候,JVM會進行回收
- 弱引用:只要進行垃圾回收,就會觸發回收
- 虛引用:不知道啥時候就被回收了,可以理解為沒引用一個樣
因此,按照JVM對他們回收的幾率從小到大依次為:
強引用<軟引用<弱引用<虛引用
也就是說JVM對強引用的回收能力最小,對虛引用的回收能力最大。
引用分類的作用
一般我們編寫的代碼都是強引用的,比如:
Person p = new Person(); Person p1 = p;p和p1都指向了創建的Person對象,他們都是強引用的。如果想要回收這個對象,只有p1和p都指向null后,才可以。
那么,有一些場景下往往引用清除的不及時,就會造成內存泄露,一些對象無法回收;無法回收的對象如果積累很多,就會造成OUT OF MEMORY——OOM.
舉個例子,在很多的代碼里面都喜歡用Map作為內存緩存的容器,如果你寫出了這樣的代碼:
Map<String,Object> map = new HashMap<String,String>(); while(true){Object value = new XXX();map.add(key,value);value = null; }雖然說,后面的value被設置成Null,但是map里面卻仍然保留了對象的引用,因此這個對象實際上是無法被回收的。
做個測試:
public class WeakTest {static final int MB = 1024 * 512;static String createLongString(int length) {StringBuilder sb = new StringBuilder(length);for (int i = 0; i < length; i++)sb.append('a');sb.append(System.nanoTime());return sb.toString();}public static void main(String[] args) {Map<Integer,String> substrings = new HashMap();//強引用的Mapfor(int i=0; i< 1000000; i++){String longStr = createLongString(MB);substrings.put(i,longStr); // longStr = null; // substrings.remove(i);System.out.println(i);}} }如果注釋的兩句話不被打開,那么很快就會內存溢出。除非你兩邊都去解除應用,可想而知,程序員做這種工作實在是太痛苦了。
不要擔心,這個時候就可以應用到上面的不同類型的引用知識了
在Java里面,JDK為我們提供了一個弱引用的集合,WeakHashMap。它會在垃圾回收的時候嘗試回收集合里面的對象。當然根據垃圾回收的時機,也可以選擇軟引用的集合。
public static void main(String[] args) {Map<Integer,String> substrings = new WeakHashMap();//弱引用的Mapfor(int i=0; i< 1000000; i++){String longStr = createLongString(MB);substrings.put(i,longStr);System.out.println(i);}}這樣就不擔心內存溢出了。
場景設想
比如,你的系統需要引用大量的資源相關的緩存,但是還沒有引入redis等緩存系統,那么就可以使用這種方式。
虛引用
虛引用的使用場景就比較雞肋了,我也想不出什么時候會使用它。但是它跟其他的引用都有一種場景,就是在垃圾回收的時候,把引用放在回收隊列里面,針對這個隊列可以做一些操作。這種方式比finalize()要文檔的多..
public class PhantomTest {public static boolean isRun = true;public static void main(String[] args) throws Exception {String abc = new String("abc");System.out.println(abc.getClass() + "@" + abc.hashCode());final ReferenceQueue referenceQueue = new ReferenceQueue<String>();new Thread() {public void run() {while (isRun) {Object o = referenceQueue.poll();if (o != null) {try {Field rereferent = Reference.class.getDeclaredField("referent");rereferent.setAccessible(true);Object result = rereferent.get(o);System.out.println("gc will collect:"+ result.getClass() + "@"+ result.hashCode());} catch (Exception e) {e.printStackTrace();}}}}}.start();PhantomReference<String> abcWeakRef = new PhantomReference<String>(abc,referenceQueue);abc = null;Thread.currentThread().sleep(3000);System.gc();Thread.currentThread().sleep(3000);isRun = false;}}首先需要創建一個引用隊列:
final ReferenceQueue referenceQueue = new ReferenceQueue<String>();創建虛引用,并關聯到引用隊列
PhantomReference<String> abcWeakRef = new PhantomReference<String>(abc,referenceQueue);等引用被回收的時候,就會在Object o = referenceQueue.poll();取到對象引用了。
雖然一般不會有這種底層的使用場景,但是了解一點總歸是好的。
本文轉自博客園xingoo的博客,原文鏈接:Java程序員的日常—— 垃圾回收中引用類型的作用,如需轉載請自行聯系原博主。總結
以上是生活随笔為你收集整理的Java程序员的日常—— 垃圾回收中引用类型的作用的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: jqGrid 中的editrules来自
- 下一篇: 看美国如何实现农业大数据的建设