java ref 应用类型_Java中的四种引用类型比较
1.引用的概念
引用這個概念是與JAVA虛擬機的垃圾回收有關的,不同的引用類型對應不同的垃圾回收策略或時機。
垃圾收集可能是大家感到難于理解的較難的概念之一,因為它并不能總是毫無遺漏地解決Java運行時環境中堆管理的問題。
垃圾回收的大致思路是:當Java虛擬機覺得內存不夠用的時候,會觸發垃圾回收操作(GC),清除無用的對象,釋放內存。可是如何判斷一個對象是否是垃圾呢?其中的一個方法是計算指向該對象的引用數量,如果引用數量為0,那么該對象就為垃圾(Thread對象是例外),否則還有用處,不能被回收。但這種引用計數算法不能解決循環引用的問題,現在JAVA虛擬機使用的是可達性分析算法。常見的情況是:一個對象并不是從根部直接引用的,而是一個對象被其他對象引用,甚至同時被幾個對象所引用,從而構成一個以根集為頂的樹形結構,當某個對象不可到達(即這個對象不能通過引用獲得這個對象時),這個對象及被這個對象所引用的其他對象都將視作垃圾,此內存接下來很可能被JVM所回收。
2.四種引用類型
Java中提供了4個級別的引用,即強引用(FinalReference)、軟引用
(SoftReference)、弱引用(WeakReference)、虛引用(PhantomReference)這四個級別。在這4個級別中只有強引用類是包內可見的,其他3種引用類型均為public,可以在應用程序中直接使用,垃圾回收器會嘗試回收只有弱引用的對象。
■強引用(Strong Reference):在一個線程內,無須引用直接可以使用的對象,強引用不會被JVM清理。我們平時申明變量使用的就是強引用,普通系統99%以上都是強引 用,比如,String s="Hello World"。
■軟引用(WeakReference):通過一個軟引用申明,JVM拋出OOM之前,清理所有的軟引用對象。垃圾回收器某個時刻決定回收軟可達的對象的時候,會清理軟引用,并可選的把引用存放到一個引用隊列(ReferenceQueue)。
■弱引用(SoftReference):通過一個弱引用申明。類似弱引用,只不過 Java 虛擬機會盡量讓軟引用的存活時間長一些,迫不得已才清理。
■虛引用(PhantomReference):通過一個虛引用申明。僅用來處理資源的清理問題,比Object里面的finalize機制更靈活。get方法返回的永遠是null,Java虛擬機不負責清理虛引用,但是它會把虛引用放到引用隊列里面。
3.強引用(Strong Reference)
Java中的引用,有點像C++的指針。通過引用,可以對堆中的對象進行操作。在Java 程序中,最常見的引用類型是強引用,它也是默認的引用類型。
當在Java語言中使用new 操作符創建一個新的對象,并將其賦值給一個變量的時候,這個變量就成為指向該對象的一個強引用。而判斷一個對象是否存活的標準為是否存在指向這個對象的引用。
在某函數中,例如:StringBuffer str=new StringBuffer(“Hello World”);假設該代碼是在函數體內運行的,那么局部變量str將被分配在棧中,而對象StringBuffer實例,被分配在堆上。局部變量str指向StringBuffer實例所在堆空間,通過str可以操作該實例,那么str就是StringBuffer的引用。此時,如果運行一個賦值語句:StringBuffer str1=str;那么,str所指向的對象也將被str1所指向,同時在局部棧空間上會分配空間存放str1變量。此時““Hello World”這個字符串對象就有了兩引用,兩個引用都同時指向這一個對象。而對引用的“==”操作用也只是表示兩個操作數所指向的堆空間地址是否相同,即判斷兩個引用是否指向同一個對象。
強引用具備以下特點:
(1)強引用可以直接訪問目標對象。
(2)強引用所指向的對象在任何時候都不會被系統回收。JVM寧愿拋出OutOf Memory異常也不會回收強引用所指向的對象。
(3)強引用可能導致內存泄漏。
強引用的存在會阻止一個對象被回收。在垃圾回收器遍歷對象引用并進行標記之后,如果一個對象是強引用可達的,那么這個對象不會作為垃圾回收的候選。當內存空間不足,Java虛擬機寧愿拋出OutOfMemoryError錯誤,使程序異常終止,也不會靠隨意回收具有強引用的對象來解決內存不足的問題。程序員編寫代碼的時候,有時為了防止內存泄漏,需要將引用顯式地賦空(Object o=null)。例如:在ArrayList的remove(int index)中,將最后一個元素賦為空。
"public E remove(int index)"方法的代碼片斷
elementData[--size] = null; //clear to let GC do its work
在實現一個緩存系統的時候,如果全部使用強引用,那么你需要自己去手動的把某些引用Clear掉(引用置為Null),否則遲早會拋出Out Of Memory錯誤。緩存系統引入弱引用或者軟引用的唯一原因是,把引用Clear的事情交由Java垃圾回收器來處理,Cache程序自己置身事外。
通常來說,應用程序內部的內存泄露有兩種情況。一種是虛擬機中存在程序無法使用的內存區域,另一種情況是程序中存在大量存活時間過長的對象。
4. 軟引用(WeakReference)
軟引用是除了強引用外最強的引用類型,我們可以通過java.lang.ref.SoftReference使用軟引用。一個持有軟引用的對象,它不會被JVM很快回收,JVM會根據當前堆的使用情況來判斷何時回收。當堆使用率臨近闕值時,才會去回收軟引用的對象。只要有足夠的內 存,軟引用便可能在內存中存活相當長一段時間。因此,就如上一節所說的,軟引用可以用于實現對內存敏感的Cache。垃圾回收器會保證在拋出OOM錯誤之前,回收掉所有軟引用可達的對象。通過軟引用,垃圾回收器就可以在內存不足時釋放軟引用可達的對象所占的內存空間。程序所要做的是保證軟引用可達的對象被垃圾回收器回收之后,程序也能正常工作。
軟引用的特點是它的一個實例保存對一個Java對象的軟引用,該軟引用的存在不妨礙垃圾收集線程對該Java對象的回收。也就是說,一旦軟引用保存了對一個Java對象的軟引用后,在垃圾線程對這個Java對象回收前,軟引用類所提供的get()方法返回Java對象的強引用。另外,一旦垃圾線程回收該Java對象之后,get()方法將返回null。
示例代碼:
importorg.junit.Assert;importjava.lang.ref.Reference;importjava.lang.ref.ReferenceQueue;importjava.lang.ref.SoftReference;public classMyObject {public static ReferenceQueuesoftQueue;
@Overrideprotected void finalize() throwsThrowable {super.finalize();
Reference obj = null;
Assert.assertNotNull(softQueue);
obj= (Reference) softQueue.remove();
System.out.println(obj);if (obj != null)
System.out.println("Object for SoftReference is" +obj.get());
System.out.println("MyObject finalize called ");//}
@OverridepublicString toString() {return "I am MyObject";
}public static voidmain(String[] args) {
MyObject obj= newMyObject();
softQueue= new ReferenceQueue<>();
SoftReference softRef = new SoftReference<>(obj, softQueue);
System.out.println(obj);
obj= null;
System.gc();
System.out.println(obj);
System.out.println("After GC:Soft get=" +softRef.get());
System.out.println("分配大塊內存");byte[] bytes = new byte[1000 * 1024 * 925];//分配一塊大內存,強制GC
System.out.println("After new byte[]:soft Get=" +softRef.get());
System.out.println(obj);
}
}
作為一個Java對象,軟引用對象除了具有保存軟引用的特殊性之外,也具有Java對象的一般性。所以,當軟引用對象被回收之后,雖然這個軟引用對象的get()方法返回null,但這個對象已經不再具有存在的價值,需要一個適當的清除機制,避免大量軟引用對象帶來的內存泄漏。在java.lang.ref包里還提供了ReferenceQueue。如果在創建軟引用對象的時候,使用了一個ReferenceQueue對象作為參數提供給軟引用的構造方法。
ReferenceQueue softQueue = new ReferenceQueue<>();
SoftReference softRef = new SoftReference<>(obj, softQueue);
那么當這個SoftReference所持有的軟引用的obj被垃圾收集器回收的同時,softRef所強引用的軟引用對象被列入ReferenceQueue。也就是說,ReferenceQueue中保存的對象是Reference對象,而且是已經失去了它所軟引用的對象的Reference對象.
ReferenceQueue是一個隊列,這具有一般隊列的基本特性,我們可以調用ReferenceQueue的poll()方法來檢查是否有它所關心的非強可及對象被回收。如果隊列為空,將返回一個null,否則該方法返回隊列中前面的一個Reference對象。利用這個方法,我們可以檢查哪個SoftReference所軟引用的對象已經被回收。于是我們可以把這些失去所軟引用的對象的SoftReference對象清除掉。常用的方式如SoftReference ref=null;while ((ref=(EmployeeRef) q.poll())!=null) {//清除ref}。
5.弱引用(SoftReference)
1)弱引用是一種比軟引用較弱的引用類型(不管系統堆空間是否足夠,都會將對象進行回收),使用弱引用后,可以維持對referent的引用,而不會阻止它被垃圾收集。當垃圾收集器跟蹤堆的時候,如果對一個對象的引用只有弱引用,那么這個referent就會成為垃圾收集的候選對象,就像沒有任何剩余的引用一樣,而且所有剩余的弱引用都被清除。弱引用是在構造時設置的,在沒有被清除之前,可以獲取它的 值,如果弱引用被清除了,無論是被垃圾收集了,還是有人調用了clear()方法,它都會返回null。因此,在使用弱引用對象之前,都需要檢查是否返回一個非null值。
2)由于垃圾回收器的線程通常優先級很低,因此并不一定能很快地發現持有弱引用的對象。在這種情況下,弱引用對象可以存在較長的時間。一旦一個弱引用對象被垃圾回收器回收,便會加入到一個注冊引用隊列中。上述例子只需要更改對象類型為weakReference即可,會看到一旦obj被設置為null,GC立即回收若類型實例。
弱引用、軟引用都非常適合來保存那些可有可無的緩存數據。如果這么做,當系統內存不足時,這些緩存數據會被回收,不會導致內存溢出。當內存資源充足時,這些緩存數據又可以存在相當長的時間,從而起到加速系統的作用。
3)弱引用的重要作用是解決對象的存活時間過長的問題。在程序中,一個對象的實際存活時間應該與它的邏輯存活時間一樣。從邏輯上來說,一個對象應該在某個方法調用完成之后就不再被需要了,可以對其進行垃圾回收。但是,如果仍然有其他的強引用存在,該對象的實際存活時間會長于邏輯存活時間,直到其他的強引用不再存在。這樣的對象在程序中過多出現會導致虛擬機的內存占用上升,最后產生OOM錯誤。要解決這樣的問題, 需要小心注意管理對象上的強引用。當不再需要引用一個對象時,顯式地清除這些強引 用。不過這會對開發人員提出更高的要求。更好的方法是使用弱引用替換強引用來引用這些對象。這樣既可以引用對象,又可以避免強引用帶來的問題。
4)Map的子類WeakHashMap就是應用弱引用的典型應用,它可以作為簡單的緩存表解決方案。WeakHashMap在java.util包內, 它實現了Map接口,是HashMap的一種實現,它使用弱引用作為內部數據的存儲方案。
如果在系統中需要一張很大的Map表,Map中的表項作為緩存之用,這也意味著即使沒有能從該Map中取得相應的數據,系統也可以通過候選方案獲取這些數據。雖然這樣會消耗更多的時間,但是不影響系統的正常運行。在這種場景下,使用WeakHashMap是最為適合的。因為WeakHashMap會在系統內存范圍內,保存所有表項,而一旦內存不夠, 在GC時沒有被引用的表項也會很快被清除掉,從而避免系統內存溢出。
@Testpublic voidmapTest() {
Map map1 = new WeakHashMap<>();long startTime =System.currentTimeMillis();
List list= newArrayList();for (int i = 0; i < 100000; i++) {
Integer iWrap=Integer.valueOf(i);
map1.put(iWrap,new byte[i]);
}
System.out.println("HashMap放入元素所耗的時間" + (System.currentTimeMillis() - startTime) / 1000 + "秒");
}
上述代碼會報出異常,堆空間錯誤,內存溢出了;
但如果將“ Map map1 = new HashMap<>();”改成“ Map map1 = new WeakHashMap<>();”,則不會出現內存溢出的問題。
另外如果在使用WeakHashMap的同時,還在循環體中加入list.add(iWrap),這仍然會出現內存溢出的問題。因為“list.add(iWrap);”這行代碼對key進行了強引用,將導致WeakHashMap不會自動啟動實例回收機制。所以,如果在系統中通過WeakHashMap自動清理數據,就盡量不要在系統的其他地方強引用WeakHashMap的key,否則這些key就不會被回收,WeakHashMap也就無法正常釋放它們所占用的內存。
5)弱引用與軟引用的區別/差異
總的來說,弱引用對象與軟引用對象的最大不同就在于,當GC在進行回收時,需要通過算法檢查是否回收軟引用對象,而對于弱引用對象,GC總是進行回收。弱引用對象更容易、更快被GC回收。雖然,GC在運行時一定回收Weak對象,但是復雜關系的弱對象群常常需要好幾次GC的運行才能完成。就像上面描述的場景,弱引用對象常常用于Map結構中,引用數據量較大的對象,一旦該對象的強引用為null時,GC能夠快速地回收該對象空間。
6.虛引用(PhantomReference)
1)虛引用,又稱幽靈引用,是強度最弱的一種引用類型,用java.lang.ref.PhantomReference類來表示。虛引用的主要目的是在一個對象所占的內存被實際回收之前得到通知,從而可以進行一些相關的清理工作。幽靈引用在使用方式上與之前介紹的兩種引用類型有很大的不同:首先虛引用在創建時必須提供一個引用隊列作為參數;其次虛引用對象的get方法總是返回null,因此無法通過虛引用來獲取被引用的對象。
2)虛引用主要用來跟蹤對象被垃圾回收器回收的活動。虛引用與軟引用和弱引用的一個區別在于:虛引用必須和引用隊列(ReferenceQueue)聯合使用。當垃圾回收器準備回收一個對象時,如果發現它還有虛引用,就會在回收對象的內存之前,把這個虛引用加入到與之關聯的引用隊列中。
3)虛引用與軟引用/弱引用的區別
軟引用和弱引用在其可達狀態達到時就可能被添加到對應的引用隊列 中。也就是說,當一個對象變成軟引用可達或弱引用可達的時候,指向這個對象的引用對象就可能被添加到引用隊列中。在添加到隊列之前,垃圾回收器會清除掉這個引用對象的引用關系。當軟引用和弱引用進入隊列之后,對象的finalize方法可能還沒有被調用。在finalize方法執行之后,該對象有可能重新回到可達狀態。如果該對象回到了可達狀態,而指向該對象的軟引用或弱引用對象的引用關系已經被清除,那么就無法再通過引用對象來查找這個對象。而幽靈引用則不同,只有在對象的finalize方法被運行之后,幽靈引用才會被添加到隊列中。與軟引用和弱引用不同的是,幽靈引用在被添加到隊列之前,垃圾回收器不會自動清除其引用關系,需要調用clear方法來顯式地清除。當幽靈引用被清除之后, 對象就進入了不可達狀態,垃圾回收器可以回收其內存。當幽靈引用被添加到隊列之后,由于PhantomReference類的get方法總是返回null,程序也不能對幽靈引用所指向的對象進行任何操作。
importjava.lang.ref.PhantomReference;importjava.lang.ref.Reference;importjava.lang.ref.ReferenceQueue;public classRefQueue {static classRefObj {
@Overrideprotected void finalize() throwsThrowable {super.finalize();
System.out.println("finalize方法被調用");
}
}public static voidphantomReferenceQueue() {
ReferenceQueue queue = new ReferenceQueue<>();
RefObj obj= newRefObj();
PhantomReference phantomRef = new PhantomReference<>(obj, queue);
obj= null;
Reference extends RefObj> ref = null;while ((ref = queue.poll()) == null) {
System.gc();
}
phantomRef.clear();
System.out.println(ref==phantomRef);
System.out.println("幽靈引用被清除");
}public static voidmain(String[] args) {
phantomReferenceQueue();
}
}
控制臺打印
總結
以上是生活随笔為你收集整理的java ref 应用类型_Java中的四种引用类型比较的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: java 调用cpp_java jni
- 下一篇: java 线程 while循环_java