自拍偷在线精品自拍偷,亚洲欧美中文日韩v在线观看不卡

面試中經(jīng)常被問到Java引用類型原理,帶你深入剖析

開發(fā) 后端
本篇文章主要是分析軟引用、弱引用、虛引用的實(shí)現(xiàn),這三種引用類型都是繼承于Reference這個(gè)類,主要邏輯也在Reference中。

1.選擇唯一性索引

唯一性索引的值是唯一的,可以更快速的通過該索引來確定某條記錄。例如,學(xué)生表中學(xué)號(hào)是具有唯一性的字段。為該字段建立唯一性索引可以很快的確定某個(gè)學(xué)生的信息。如果使用姓名的話,可能存在同名現(xiàn)象,從而降低查詢速度。

2.為經(jīng)常需要排序、分組和聯(lián)合操作的字段建立索引

經(jīng)常需要ORDER BY、GROUP BY、DISTINCT和UNION等操作的字段,排序操作會(huì)浪費(fèi)很多時(shí)間。如果為其建立索引,可以有效地避免排序操作。

3.為常作為查詢條件的字段建立索引

如果某個(gè)字段經(jīng)常用來做查詢條件,那么該字段的查詢速度會(huì)影響整個(gè)表的查詢速度。因此,為這樣的字段建立索引,可以提高整個(gè)表的查詢速度。

4.限制索引的數(shù)目

索引的數(shù)目不是越多越好。每個(gè)索引都需要占用磁盤空間,索引越多,需要的磁盤空間就越大。修改表時(shí),對索引的重構(gòu)和更新很麻煩。越多的索引,會(huì)使更新表變得很浪費(fèi)時(shí)間。

5.盡量使用數(shù)據(jù)量少的索引

如果索引的值很長,那么查詢的速度會(huì)受到影響。例如,對一個(gè)CHAR(100)類型的字段進(jìn)行全文檢索需要的時(shí)間肯定要比對CHAR(10)類型的字段需要的時(shí)間要多。

6.盡量使用前綴來索引

如果索引字段的值很長,最好使用值的前綴來索引。例如,TEXT和BLOG類型的字段,進(jìn)行全文檢索會(huì)很浪費(fèi)時(shí)間。如果只檢索字段的前面的若干個(gè)字符,這樣可以提高檢索速度。

7.刪除不再使用或者很少使用的索引

表中的數(shù)據(jù)被大量更新,或者數(shù)據(jù)的使用方式被改變后,原有的一些索引可能不再需要。數(shù)據(jù)庫管理員應(yīng)當(dāng)定期找出這些索引,將它們刪除,從而減少索引對更新操作的影響。

8.最左前綴匹配原則,非常重要的原則。

mysql會(huì)一直向右匹配直到遇到范圍查詢(>、<、between、like)就停止匹配,比如a 1=”” and=”” b=”2” c=”“> 3 and d = 4 如果建立(a,b,c,d)順序的索引,d是用不到索引的,如果建立(a,b,d,c)的索引則都可以用到,a,b,d的順序可以任意調(diào)整。

9.=和in可以亂序。

比如a = 1 and b = 2 and c = 3 建立(a,b,c)索引可以任意順序,mysql的查詢優(yōu)化器會(huì)幫你優(yōu)化成索引可以識(shí)別的形式

10.盡量選擇區(qū)分度高的列作為索引。

區(qū)分度的公式是count(distinct col)/count(*),表示字段不重復(fù)的比例,比例越大我們掃描的記錄數(shù)越少,唯一鍵的區(qū)分度是1,而一些狀態(tài)、性別字段可能在大數(shù)據(jù)面前區(qū)分度就 是0,那可能有人會(huì)問,這個(gè)比例有什么經(jīng)驗(yàn)值嗎?使用場景不同,這個(gè)值也很難確定,一般需要join的字段我們都要求是0.1以上,即平均1條掃描10條 記錄

11.索引列不能參與計(jì)算,保持列“干凈”。

比如from_unixtime(create_time) = ’2014-05-29’就不能使用到索引,原因很簡單,b+樹中存的都是數(shù)據(jù)表中的字段值,但進(jìn)行檢索時(shí),需要把所有元素都應(yīng)用函數(shù)才能比較,顯然成本 太大。所以語句應(yīng)該寫成create_time = unix_timestamp(’2014-05-29’);

12.盡量的擴(kuò)展索引,不要新建索引。

比如表中已經(jīng)有a的索引,現(xiàn)在要加(a,b)的索引,那么只需要修改原來的索引即可

注意:選擇索引的最終目的是為了使查詢的速度變快。上面給出的原則是最基本的準(zhǔn)則,但不能拘泥于上面的準(zhǔn)則。讀者要在以后的學(xué)習(xí)和工作中進(jìn)行不斷的實(shí)踐。根據(jù)應(yīng)用的實(shí)際情況進(jìn)行分析和判斷,選擇最合適的索引方式。

Java中一共有4種引用類型(其實(shí)還有一些其他的引用類型比如FinalReference):強(qiáng)引用、軟引用、弱引用、虛引用。

其中強(qiáng)引用就是我們經(jīng)常使用的Object a = new Object(); 這樣的形式,在Java中并沒有對應(yīng)的Reference類。

本篇文章主要是分析軟引用、弱引用、虛引用的實(shí)現(xiàn),這三種引用類型都是繼承于Reference這個(gè)類,主要邏輯也在Reference中。

問題

在分析前,先拋幾個(gè)問題?

  1. 網(wǎng)上大多數(shù)文章對于軟引用的介紹是:在內(nèi)存不足的時(shí)候才會(huì)被回收,那內(nèi)存不足是怎么定義的?什么才叫內(nèi)存不足?
  2. 網(wǎng)上大多數(shù)文章對于虛引用的介紹是:形同虛設(shè),虛引用并不會(huì)決定對象的生命周期。主要用來跟蹤對象被垃圾回收器回收的活動(dòng)。真的是這樣嗎?
  3. 虛引用在Jdk中有哪些場景下用到了呢?

Reference

我們先看下Reference.java中的幾個(gè)字段

  1. public abstract class Reference<T> { 
  2.     //引用的對象 
  3.     private T referent;         
  4.     //回收隊(duì)列,由使用者在Reference的構(gòu)造函數(shù)中指定 
  5.     volatile ReferenceQueue<? super T> queue; 
  6.      //當(dāng)該引用被加入到queue中的時(shí)候,該字段被設(shè)置為queue中的下一個(gè)元素,以形成鏈表結(jié)構(gòu) 
  7.     volatile Reference next
  8.     //在GC時(shí),JVM底層會(huì)維護(hù)一個(gè)叫DiscoveredList的鏈表,存放的是Reference對象,discovered字段指向的就是鏈表中的下一個(gè)元素,由JVM設(shè)置 
  9.     transient private Reference<T> discovered;   
  10.     //進(jìn)行線程同步的鎖對象 
  11.     static private class Lock { } 
  12.     private static Lock lock = new Lock(); 
  13.     //等待加入queue的Reference對象,在GC時(shí)由JVM設(shè)置,會(huì)有一個(gè)java層的線程(ReferenceHandler)源源不斷的從pending中提取元素加入到queue 
  14.     private static Reference<Object> pending = null

一個(gè)Reference對象的生命周期如下:

主要分為Native層和Java層兩個(gè)部分。

Native層在GC時(shí)將需要被回收的Reference對象加入到DiscoveredList中(代碼在referenceProcessor.cpp中

process_discovered_references方法),然后將DiscoveredList的元素移動(dòng)到PendingList中(代碼在referenceProcessor.cpp中enqueue_discovered_ref_helper方法),PendingList的隊(duì)首就是Reference類中的pending對象。

看看Java層的代碼

  1. private static class ReferenceHandler extends Thread { 
  2.          ... 
  3.         public void run() { 
  4.             while (true) { 
  5.                 tryHandlePending(true); 
  6.             } 
  7.         } 
  8.   }  
  9. static boolean tryHandlePending(boolean waitForNotify) { 
  10.         Reference<Object> r; 
  11.         Cleaner c; 
  12.         try { 
  13.             synchronized (lock) { 
  14.                 if (pending != null) { 
  15.                     r = pending; 
  16.                      //如果是Cleaner對象,則記錄下來,下面做特殊處理 
  17.                     c = r instanceof Cleaner ? (Cleaner) r : null
  18.                     //指向PendingList的下一個(gè)對象 
  19.                     pending = r.discovered; 
  20.                     r.discovered = null
  21.                 } else { 
  22.                    //如果pending為null就先等待,當(dāng)有對象加入到PendingList中時(shí),jvm會(huì)執(zhí)行notify 
  23.                     if (waitForNotify) { 
  24.                         lock.wait(); 
  25.                     } 
  26.                     // retry if waited 
  27.                     return waitForNotify; 
  28.                 } 
  29.             } 
  30.         }  
  31.         ... 
  32.  
  33.         // 如果時(shí)CLeaner對象,則調(diào)用clean方法進(jìn)行資源回收 
  34.         if (c != null) { 
  35.             c.clean(); 
  36.             return true
  37.         } 
  38.         //將Reference加入到ReferenceQueue,開發(fā)者可以通過從ReferenceQueue中poll元素感知到對象被回收的事件。 
  39.         ReferenceQueue<? super Object> q = r.queue; 
  40.         if (q != ReferenceQueue.NULL) q.enqueue(r); 
  41.         return true
  42.  } 

流程比較簡單:就是源源不斷的從PendingList中提取出元素,然后將其加入到ReferenceQueue中去,開發(fā)者可以通過從ReferenceQueue中poll元素感知到對象被回收的事件。

另外需要注意的是,對于Cleaner類型(繼承自虛引用)的對象會(huì)有額外的處理:在其指向的對象被回收時(shí),會(huì)調(diào)用clean方法,該方法主要是用來做對應(yīng)的資源回收,在堆外內(nèi)存DirectByteBuffer中就是用Cleaner進(jìn)行堆外內(nèi)存的回收,這也是虛引用在java中的典型應(yīng)用。

看完了Reference的實(shí)現(xiàn),再看看幾個(gè)實(shí)現(xiàn)類里,各自有什么不同。

SoftReference

  1. public class SoftReference<T> extends Reference<T> { 
  2.  
  3.     static private long clock; 
  4.  
  5.     private long timestamp
  6.  
  7.     public SoftReference(T referent) { 
  8.         super(referent); 
  9.         this.timestamp = clock; 
  10.     } 
  11.  
  12.     public SoftReference(T referent, ReferenceQueue<? super T> q) { 
  13.         super(referent, q); 
  14.         this.timestamp = clock; 
  15.     } 
  16.  
  17.     public T get() { 
  18.         T o = super.get(); 
  19.         if (o != null && this.timestamp != clock) 
  20.             this.timestamp = clock; 
  21.         return o; 
  22.     } 
  23.  

軟引用的實(shí)現(xiàn)很簡單,就多了兩個(gè)字段:clock和timestamp。clock是個(gè)靜態(tài)變量,每次GC時(shí)都會(huì)將該字段設(shè)置成當(dāng)前時(shí)間。timestamp字段則會(huì)在每次調(diào)用get方法時(shí)將其賦值為clock(如果不相等且對象沒被回收)。

那這兩個(gè)字段的作用是什么呢?這和軟引用在內(nèi)存不夠的時(shí)候才被回收,又有什么關(guān)系呢?

這些還得看JVM的源碼才行,因?yàn)闆Q定對象是否需要被回收都是在GC中實(shí)現(xiàn)的。

  1. size_t 
  2. ReferenceProcessor::process_discovered_reflist( 
  3.   DiscoveredList               refs_lists[], 
  4.   ReferencePolicy*             policy, 
  5.   bool                         clear_referent, 
  6.   BoolObjectClosure*           is_alive, 
  7.   OopClosure*                  keep_alive, 
  8.   VoidClosure*                 complete_gc, 
  9.   AbstractRefProcTaskExecutor* task_executor) 
  10.  ... 
  11.    //還記得上文提到過的DiscoveredList嗎?refs_lists就是DiscoveredList。 
  12.    //對于DiscoveredList的處理分為幾個(gè)階段,SoftReference的處理就在第一階段 
  13.  ... 
  14.       for (uint i = 0; i < _max_num_q; i++) { 
  15.         process_phase1(refs_lists[i], policy, 
  16.                        is_alive, keep_alive, complete_gc); 
  17.       } 
  18.  ... 
  19.  
  20. //該階段的主要目的就是當(dāng)內(nèi)存足夠時(shí),將對應(yīng)的SoftReference從refs_list中移除。 
  21. void 
  22. ReferenceProcessor::process_phase1(DiscoveredList&    refs_list, 
  23.                                    ReferencePolicy*   policy, 
  24.                                    BoolObjectClosure* is_alive, 
  25.                                    OopClosure*        keep_alive, 
  26.                                    VoidClosure*       complete_gc) { 
  27.  
  28.   DiscoveredListIterator iter(refs_list, keep_alive, is_alive); 
  29.   // Decide which softly reachable refs should be kept alive. 
  30.   while (iter.has_next()) { 
  31.     iter.load_ptrs(DEBUG_ONLY(!discovery_is_atomic() /* allow_null_referent */)); 
  32.     //判斷引用的對象是否存活 
  33.     bool referent_is_dead = (iter.referent() != NULL) && !iter.is_referent_alive(); 
  34.     //如果引用的對象已經(jīng)不存活了,則會(huì)去調(diào)用對應(yīng)的ReferencePolicy判斷該對象是不時(shí)要被回收 
  35.     if (referent_is_dead && 
  36.         !policy->should_clear_reference(iter.obj(), _soft_ref_timestamp_clock)) { 
  37.       if (TraceReferenceGC) { 
  38.         gclog_or_tty->print_cr("Dropping reference (" INTPTR_FORMAT ": %s"  ") by policy"
  39.                                (void *)iter.obj(), iter.obj()->klass()->internal_name()); 
  40.       } 
  41.       // Remove Reference object from list 
  42.       iter.remove(); 
  43.       // Make the Reference object active again 
  44.       iter.make_active(); 
  45.       // keep the referent around 
  46.       iter.make_referent_alive(); 
  47.       iter.move_to_next(); 
  48.     } else { 
  49.       iter.next(); 
  50.     } 
  51.   } 
  52.  ... 

refs_lists中存放了本次GC發(fā)現(xiàn)的某種引用類型(虛引用、軟引用、弱引用等),而

process_discovered_reflist方法的作用就是將不需要被回收的對象從refs_lists移除掉,refs_lists最后剩下的元素全是需要被回收的元素,最后會(huì)將其第一個(gè)元素賦值給上文提到過的Reference.java#pending字段。

ReferencePolicy一共有4種實(shí)現(xiàn):NeverClearPolicy,AlwaysClearPolicy,LRUCurrentHeapPolicy,LRUMaxHeapPolicy。

其中NeverClearPolicy永遠(yuǎn)返回false,代表永遠(yuǎn)不回收SoftReference,在JVM中該類沒有被使用,AlwaysClearPolicy則永遠(yuǎn)返回true,在referenceProcessor.hpp#setup方法中中可以設(shè)置policy為AlwaysClearPolicy,至于什么時(shí)候會(huì)用到AlwaysClearPolicy,大家有興趣可以自行研究。

LRUCurrentHeapPolicy和LRUMaxHeapPolicy的should_clear_reference方法則是完全相同:

  1. bool LRUMaxHeapPolicy::should_clear_reference(oop p, 
  2.                                              jlong timestamp_clock) { 
  3.   jlong interval = timestamp_clock - java_lang_ref_SoftReference::timestamp(p); 
  4.   assert(interval >= 0, "Sanity check"); 
  5.  
  6.   // The interval will be zero if the ref was accessed since the last scavenge/gc. 
  7.   if(interval <= _max_interval) { 
  8.     return false
  9.   } 
  10.  
  11.   return true

timestamp_clock就是SoftReference的靜態(tài)字段clock,

java_lang_ref_SoftReference::timestamp(p)對應(yīng)是字段timestamp。如果上次GC后有調(diào)用SoftReference#get,interval值為0,否則為若干次GC之間的時(shí)間差。

_max_interval則代表了一個(gè)臨界值,它的值在LRUCurrentHeapPolicy和LRUMaxHeapPolicy兩種策略中有差異。

  1. void LRUCurrentHeapPolicy::setup() { 
  2.   _max_interval = (Universe::get_heap_free_at_last_gc() / M) * SoftRefLRUPolicyMSPerMB; 
  3.   assert(_max_interval >= 0,"Sanity check"); 
  4.  
  5. void LRUMaxHeapPolicy::setup() { 
  6.   size_t max_heap = MaxHeapSize; 
  7.   max_heap -= Universe::get_heap_used_at_last_gc(); 
  8.   max_heap /= M; 
  9.  
  10.   _max_interval = max_heap * SoftRefLRUPolicyMSPerMB; 
  11.   assert(_max_interval >= 0,"Sanity check"); 

看到這里你就知道SoftReference到底什么時(shí)候被被回收了,它和使用的策略(默認(rèn)應(yīng)該是LRUCurrentHeapPolicy),堆可用大小,該SoftReference上一次調(diào)用get方法的時(shí)間都有關(guān)系。

WeakReference

  1. public class WeakReference<T> extends Reference<T> { 
  2.  
  3.     public WeakReference(T referent) { 
  4.         super(referent); 
  5.     } 
  6.  
  7.     public WeakReference(T referent, ReferenceQueue<? super T> q) { 
  8.         super(referent, q); 
  9.     } 
  10.  

可以看到,對于Soft references和Weak references clear_referent字段傳入的都是true,這也符合我們的預(yù)期:對象不可達(dá)后,引用字段就會(huì)被置為null,然后對象就會(huì)被回收(對于軟引用來說,如果內(nèi)存足夠的話,在Phase 1,相關(guān)的引用就會(huì)從refs_list中被移除,到Phase 3時(shí)refs_list為空集合)。

但對于Final references和 Phantom references,clear_referent字段傳入的是false,也就意味著被這兩種引用類型引用的對象,如果沒有其他額外處理,只要Reference對象還存活,那引用的對象是不會(huì)被回收的。Final references和對象是否重寫了finalize方法有關(guān),不在本文分析范圍之內(nèi),我們接下來看看Phantom references。

可以看到WeakReference在Java層只是繼承了Reference,沒有做任何的改動(dòng)。那referent字段是什么時(shí)候被置為null的呢?要搞清楚這個(gè)問題我們再看下上文提到過的

process_discovered_reflist方法:

 

  1. size_t 
  2. ReferenceProcessor::process_discovered_reflist( 
  3.   DiscoveredList               refs_lists[], 
  4.   ReferencePolicy*             policy, 
  5.   bool                         clear_referent, 
  6.   BoolObjectClosure*           is_alive, 
  7.   OopClosure*                  keep_alive, 
  8.   VoidClosure*                 complete_gc, 
  9.   AbstractRefProcTaskExecutor* task_executor) 
  10.  ... 
  11.  
  12.   //Phase 1:將所有不存活但是還不能被回收的軟引用從refs_lists中移除(只有refs_lists為軟引用的時(shí)候,這里policy才不為null) 
  13.   if (policy != NULL) { 
  14.     if (mt_processing) { 
  15.       RefProcPhase1Task phase1(*this, refs_lists, policy, true /*marks_oops_alive*/); 
  16.       task_executor->execute(phase1); 
  17.     } else { 
  18.       for (uint i = 0; i < _max_num_q; i++) { 
  19.         process_phase1(refs_lists[i], policy, 
  20.                        is_alive, keep_alive, complete_gc); 
  21.       } 
  22.     } 
  23.   } else { // policy == NULL 
  24.     assert(refs_lists != _discoveredSoftRefs, 
  25.            "Policy must be specified for soft references."); 
  26.   } 
  27.  
  28.   // Phase 2: 
  29.   // 移除所有指向?qū)ο筮€存活的引用 
  30.   if (mt_processing) { 
  31.     RefProcPhase2Task phase2(*this, refs_lists, !discovery_is_atomic() /*marks_oops_alive*/); 
  32.     task_executor->execute(phase2); 
  33.   } else { 
  34.     for (uint i = 0; i < _max_num_q; i++) { 
  35.       process_phase2(refs_lists[i], is_alive, keep_alive, complete_gc); 
  36.     } 
  37.   } 
  38.  
  39.   // Phase 3: 
  40.   // 根據(jù)clear_referent的值決定是否將不存活對象回收 
  41.   if (mt_processing) { 
  42.     RefProcPhase3Task phase3(*this, refs_lists, clear_referent, true /*marks_oops_alive*/); 
  43.     task_executor->execute(phase3); 
  44.   } else { 
  45.     for (uint i = 0; i < _max_num_q; i++) { 
  46.       process_phase3(refs_lists[i], clear_referent, 
  47.                      is_alive, keep_alive, complete_gc); 
  48.     } 
  49.   } 
  50.  
  51.   return total_list_count; 
  52.  
  53. void 
  54. ReferenceProcessor::process_phase3(DiscoveredList&    refs_list, 
  55.                                    bool               clear_referent, 
  56.                                    BoolObjectClosure* is_alive, 
  57.                                    OopClosure*        keep_alive, 
  58.                                    VoidClosure*       complete_gc) { 
  59.   ResourceMark rm; 
  60.   DiscoveredListIterator iter(refs_list, keep_alive, is_alive); 
  61.   while (iter.has_next()) { 
  62.     iter.update_discovered(); 
  63.     iter.load_ptrs(DEBUG_ONLY(false /* allow_null_referent */)); 
  64.     if (clear_referent) { 
  65.       // NULL out referent pointer 
  66.       //將Reference的referent字段置為null,之后會(huì)被GC回收 
  67.       iter.clear_referent(); 
  68.     } else { 
  69.       // keep the referent around 
  70.       //標(biāo)記引用的對象為存活,該對象在這次GC將不會(huì)被回收 
  71.       iter.make_referent_alive(); 
  72.     } 
  73.     ... 
  74.   } 
  75.     ... 

不管是弱引用還是其他引用類型,將字段referent置null的操作都發(fā)生在process_phase3中,而具體行為是由clear_referent的值決定的。而clear_referent的值則和引用類型相關(guān)。

 

  1. ReferenceProcessorStats ReferenceProcessor::process_discovered_references( 
  2.   BoolObjectClosure*           is_alive, 
  3.   OopClosure*                  keep_alive, 
  4.   VoidClosure*                 complete_gc, 
  5.   AbstractRefProcTaskExecutor* task_executor, 
  6.   GCTimer*                     gc_timer) { 
  7.   NOT_PRODUCT(verify_ok_to_handle_reflists()); 
  8.     ... 
  9.   //process_discovered_reflist方法的第3個(gè)字段就是clear_referent 
  10.   // Soft references 
  11.   size_t soft_count = 0; 
  12.   { 
  13.     GCTraceTime tt("SoftReference", trace_time, false, gc_timer); 
  14.     soft_count = 
  15.       process_discovered_reflist(_discoveredSoftRefs, _current_soft_ref_policy, true
  16.                                  is_alive, keep_alive, complete_gc, task_executor); 
  17.   } 
  18.  
  19.   update_soft_ref_master_clock(); 
  20.  
  21.   // Weak references 
  22.   size_t weak_count = 0; 
  23.   { 
  24.     GCTraceTime tt("WeakReference", trace_time, false, gc_timer); 
  25.     weak_count = 
  26.       process_discovered_reflist(_discoveredWeakRefs, NULLtrue
  27.                                  is_alive, keep_alive, complete_gc, task_executor); 
  28.   } 
  29.  
  30.   // Final references 
  31.   size_t final_count = 0; 
  32.   { 
  33.     GCTraceTime tt("FinalReference", trace_time, false, gc_timer); 
  34.     final_count = 
  35.       process_discovered_reflist(_discoveredFinalRefs, NULLfalse
  36.                                  is_alive, keep_alive, complete_gc, task_executor); 
  37.   } 
  38.  
  39.   // Phantom references 
  40.   size_t phantom_count = 0; 
  41.   { 
  42.     GCTraceTime tt("PhantomReference", trace_time, false, gc_timer); 
  43.     phantom_count = 
  44.       process_discovered_reflist(_discoveredPhantomRefs, NULLfalse
  45.                                  is_alive, keep_alive, complete_gc, task_executor); 
  46.   } 
  47.     ... 

可以看到,對于Soft references和Weak references clear_referent字段傳入的都是true,這也符合我們的預(yù)期:對象不可達(dá)后,引用字段就會(huì)被置為null,然后對象就會(huì)被回收(對于軟引用來說,如果內(nèi)存足夠的話,在Phase 1,相關(guān)的引用就會(huì)從refs_list中被移除,到Phase 3時(shí)refs_list為空集合)。

但對于Final references和 Phantom references,clear_referent字段傳入的是false,也就意味著被這兩種引用類型引用的對象,如果沒有其他額外處理,只要Reference對象還存活,那引用的對象是不會(huì)被回收的。Final references和對象是否重寫了finalize方法有關(guān),不在本文分析范圍之內(nèi),我們接下來看看Phantom references。

PhantomReference

 

  1. public class PhantomReference<T> extends Reference<T> { 
  2.  
  3.     public T get() { 
  4.         return null
  5.     } 
  6.  
  7.     public PhantomReference(T referent, ReferenceQueue<? super T> q) { 
  8.         super(referent, q); 
  9.     } 
  10.  

可以看到虛引用的get方法永遠(yuǎn)返回null,我們看個(gè)demo。

  1. public static void demo() throws InterruptedException { 
  2.         Object obj = new Object(); 
  3.         ReferenceQueue<Object> refQueue =new ReferenceQueue<>(); 
  4.         PhantomReference<Object> phanRef =new PhantomReference<>(obj, refQueue); 
  5.  
  6.         Object objg = phanRef.get(); 
  7.         //這里拿到的是null 
  8.         System.out.println(objg); 
  9.         //讓obj變成垃圾 
  10.         obj=null
  11.         System.gc(); 
  12.         Thread.sleep(3000); 
  13.         //gc后會(huì)將phanRef加入到refQueue中 
  14.         Reference<? extends Object> phanRefP = refQueue.remove(); 
  15.          //這里輸出true 
  16.         System.out.println(phanRefP==phanRef); 
  17.     } 

從以上代碼中可以看到,虛引用能夠在指向?qū)ο蟛豢蛇_(dá)時(shí)得到一個(gè)'通知'(其實(shí)所有繼承References的類都有這個(gè)功能),需要注意的是GC完成后,phanRef.referent依然指向之前創(chuàng)建Object,也就是說Object對象一直沒被回收!

而造成這一現(xiàn)象的原因在上一小節(jié)末尾已經(jīng)說了:對于Final references和 Phantom references,clear_referent字段傳入的時(shí)false,也就意味著被這兩種引用類型引用的對象,如果沒有其他額外處理,在GC中是不會(huì)被回收的。

對于虛引用來說,從refQueue.remove();得到引用對象后,可以調(diào)用clear方法強(qiáng)行解除引用和對象之間的關(guān)系,使得對象下次可以GC時(shí)可以被回收掉。

End

針對文章開頭提出的幾個(gè)問題,看完分析,我們已經(jīng)能給出回答:

1.我們經(jīng)常在網(wǎng)上看到軟引用的介紹是:在內(nèi)存不足的時(shí)候才會(huì)回收,那內(nèi)存不足是怎么定義的?為什么才叫內(nèi)存不足?

軟引用會(huì)在內(nèi)存不足時(shí)被回收,內(nèi)存不足的定義和該引用對象get的時(shí)間以及當(dāng)前堆可用內(nèi)存大小都有關(guān)系,計(jì)算公式在上文中也已經(jīng)給出。

2.網(wǎng)上對于虛引用的介紹是:形同虛設(shè),與其他幾種引用都不同,虛引用并不會(huì)決定對象的生命周期。主要用來跟蹤對象被垃圾回收器回收的活動(dòng)。真的是這樣嗎?

嚴(yán)格的說,虛引用是會(huì)影響對象生命周期的,如果不做任何處理,只要虛引用不被回收,那其引用的對象永遠(yuǎn)不會(huì)被回收。所以一般來說,從ReferenceQueue中獲得PhantomReference對象后,如果PhantomReference對象不會(huì)被回收的話(比如被其他GC ROOT可達(dá)的對象引用),需要調(diào)用clear方法解除PhantomReference和其引用對象的引用關(guān)系。

3.虛引用在Jdk中有哪些場景下用到了呢?

DirectByteBuffer中是用虛引用的子類Cleaner.java來實(shí)現(xiàn)堆外內(nèi)存回收的,后續(xù)會(huì)寫篇文章來說說堆外內(nèi)存的里里外外。

 

責(zé)任編輯:未麗燕 來源: 今日頭條
相關(guān)推薦

2018-02-01 09:26:12

面試算法題程序員

2019-03-06 14:26:31

Javascript面試前端

2019-02-21 10:49:51

Redis持久化恢復(fù)

2019-12-16 15:37:57

JavaScript人生第一份工作瀏覽器

2019-07-16 10:10:46

JavaScript數(shù)據(jù)類型

2022-10-09 07:33:38

JavaSPI程序

2009-09-11 11:09:36

C#引用類型

2022-08-12 09:35:36

JavaScript面試

2020-05-14 08:13:56

JDK命令Java

2019-05-15 16:45:13

SpringBoot面試題Java

2024-06-28 09:07:19

2017-11-29 14:41:48

神經(jīng)網(wǎng)絡(luò)遞歸神經(jīng)網(wǎng)絡(luò)RNN

2010-04-06 13:07:45

Oracle數(shù)據(jù)庫

2009-09-11 11:17:04

C#引用類型

2020-07-01 17:25:28

Redis數(shù)據(jù)庫內(nèi)存

2009-03-26 10:33:34

Oracle數(shù)據(jù)塊數(shù)據(jù)庫

2009-03-06 16:48:23

數(shù)據(jù)塊原理Oracle

2022-04-01 12:40:13

MySQL數(shù)據(jù)庫

2010-09-17 15:32:52

JVM工作原理

2022-09-05 22:22:00

Stream操作對象
點(diǎn)贊
收藏

51CTO技術(shù)棧公眾號(hào)