精品欧美一区二区三区在线观看 _久久久久国色av免费观看性色_国产精品久久在线观看_亚洲第一综合网站_91精品又粗又猛又爽_小泽玛利亚一区二区免费_91亚洲精品国偷拍自产在线观看 _久久精品视频在线播放_美女精品久久久_欧美日韩国产成人在线

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

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

1.選擇唯一性索引

唯一性索引的值是唯一的,可以更快速的通過(guò)該索引來(lái)確定某條記錄。例如,學(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)常用來(lái)做查詢條件,那么該字段的查詢速度會(huì)影響整個(gè)表的查詢速度。因此,為這樣的字段建立索引,可以提高整個(gè)表的查詢速度。

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

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

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

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

6.盡量使用前綴來(lái)索引

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

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

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

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ì)問(wèn),這個(gè)比例有什么經(jīng)驗(yàn)值嗎?使用場(chǎng)景不同,這個(gè)值也很難確定,一般需要join的字段我們都要求是0.1以上,即平均1條掃描10條 記錄

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

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

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

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

注意:選擇索引的最終目的是為了使查詢的速度變快。上面給出的原則是最基本的準(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中并沒(méi)有對(duì)應(yīng)的Reference類。

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

問(wèn)題

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

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

Reference

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

  1. public abstract class Reference<T> { 
  2.     //引用的對(duì)象 
  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對(duì)象,discovered字段指向的就是鏈表中的下一個(gè)元素,由JVM設(shè)置 
  9.     transient private Reference<T> discovered;   
  10.     //進(jìn)行線程同步的鎖對(duì)象 
  11.     static private class Lock { } 
  12.     private static Lock lock = new Lock(); 
  13.     //等待加入queue的Reference對(duì)象,在GC時(shí)由JVM設(shè)置,會(huì)有一個(gè)java層的線程(ReferenceHandler)源源不斷的從pending中提取元素加入到queue 
  14.     private static Reference<Object> pending = null

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

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

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

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

看看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對(duì)象,則記錄下來(lái),下面做特殊處理 
  17.                     c = r instanceof Cleaner ? (Cleaner) r : null
  18.                     //指向PendingList的下一個(gè)對(duì)象 
  19.                     pending = r.discovered; 
  20.                     r.discovered = null
  21.                 } else { 
  22.                    //如果pending為null就先等待,當(dāng)有對(duì)象加入到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對(duì)象,則調(diào)用clean方法進(jìn)行資源回收 
  34.         if (c != null) { 
  35.             c.clean(); 
  36.             return true
  37.         } 
  38.         //將Reference加入到ReferenceQueue,開(kāi)發(fā)者可以通過(guò)從ReferenceQueue中poll元素感知到對(duì)象被回收的事件。 
  39.         ReferenceQueue<? super Object> q = r.queue; 
  40.         if (q != ReferenceQueue.NULL) q.enqueue(r); 
  41.         return true
  42.  } 

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

另外需要注意的是,對(duì)于Cleaner類型(繼承自虛引用)的對(duì)象會(huì)有額外的處理:在其指向的對(duì)象被回收時(shí),會(huì)調(diào)用clean方法,該方法主要是用來(lái)做對(duì)應(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)很簡(jiǎn)單,就多了兩個(gè)字段:clock和timestamp。clock是個(gè)靜態(tài)變量,每次GC時(shí)都會(huì)將該字段設(shè)置成當(dāng)前時(shí)間。timestamp字段則會(huì)在每次調(diào)用get方法時(shí)將其賦值為clock(如果不相等且對(duì)象沒(méi)被回收)。

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

這些還得看JVM的源碼才行,因?yàn)闆Q定對(duì)象是否需要被回收都是在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.    //還記得上文提到過(guò)的DiscoveredList嗎?refs_lists就是DiscoveredList。 
  12.    //對(duì)于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í),將對(duì)應(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.     //判斷引用的對(duì)象是否存活 
  33.     bool referent_is_dead = (iter.referent() != NULL) && !iter.is_referent_alive(); 
  34.     //如果引用的對(duì)象已經(jīng)不存活了,則會(huì)去調(diào)用對(duì)應(yīng)的ReferencePolicy判斷該對(duì)象是不時(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方法的作用就是將不需要被回收的對(duì)象從refs_lists移除掉,refs_lists最后剩下的元素全是需要被回收的元素,最后會(huì)將其第一個(gè)元素賦值給上文提到過(guò)的Reference.java#pending字段。

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

其中NeverClearPolicy永遠(yuǎn)返回false,代表永遠(yuǎn)不回收SoftReference,在JVM中該類沒(méi)有被使用,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)對(duì)應(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.  

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

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

可以看到WeakReference在Java層只是繼承了Reference,沒(méi)有做任何的改動(dòng)。那referent字段是什么時(shí)候被置為null的呢?要搞清楚這個(gè)問(wèn)題我們?cè)倏聪律衔奶岬竭^(guò)的

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的值決定是否將不存活對(duì)象回收 
  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)記引用的對(duì)象為存活,該對(duì)象在這次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.     ... 

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

但對(duì)于Final references和 Phantom references,clear_referent字段傳入的是false,也就意味著被這兩種引用類型引用的對(duì)象,如果沒(méi)有其他額外處理,只要Reference對(duì)象還存活,那引用的對(duì)象是不會(huì)被回收的。Final references和對(duì)象是否重寫(xiě)了finalize方法有關(guān),不在本文分析范圍之內(nèi),我們接下來(lá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,也就是說(shuō)Object對(duì)象一直沒(méi)被回收!

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

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

End

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

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

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

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

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

3.虛引用在Jdk中有哪些場(chǎng)景下用到了呢?

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

 

責(zé)任編輯:未麗燕 來(lái)源: 今日頭條
相關(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

2017-11-29 14:41:48

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

2024-06-28 09:07:19

2020-07-01 17:25:28

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

2010-04-06 13:07:45

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

2009-09-11 11:17:04

C#引用類型

2009-03-06 16:48:23

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

2009-03-26 10:33:34

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

2022-04-01 12:40:13

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

2022-09-05 22:22:00

Stream操作對(duì)象

2010-09-17 15:32:52

JVM工作原理
點(diǎn)贊
收藏

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

日本aⅴ亚洲精品中文乱码| 成人久久精品| 国产精品女主播在线观看| 国产一区欧美二区三区| 久久精品欧美一区二区| 久久成人高清| 日韩一级成人av| 精品一区二区中文字幕| 免费网站看v片在线a| 99视频国产精品| 91精品久久久久久久久久久久久| 日韩av电影网址| 色琪琪久久se色| 日韩成人av在线| 天堂av.com| 欧美日韩视频免费观看| 亚洲一区二区三区视频在线 | 久久日韩精品一区二区五区| 国产欧美日韩精品在线观看 | 国产欧美丝祙| 欧美乱大交xxxxx| 夜夜春很很躁夜夜躁| 精品亚洲自拍| 日韩欧美一二区| 污视频网站观看| 网友自拍亚洲| 亚欧色一区w666天堂| 香蕉精品视频在线| 国产区av在线| 91蜜桃免费观看视频| 亚洲一区二区中文| 中文字幕久久久久| 老鸭窝毛片一区二区三区| 欧美国产高跟鞋裸体秀xxxhd| 欧美综合第一页| 欧美 日本 国产| 盗摄牛牛av影视一区二区| 91麻豆精品91久久久久同性| mm131亚洲精品| 91欧美精品| 在线一区二区视频| 成人观看免费完整观看| 亚洲天堂手机| 精品福利免费观看| 性欧美大战久久久久久久| 中文字幕中文字幕在线中高清免费版| 成人免费视频在线观看| 中文字幕日韩一区二区三区 | 欧美国产精品中文字幕| 日本精品一区二区三区视频| 免费看男男www网站入口在线| 99这里只有久久精品视频| 国产日韩二区| 日本私人网站在线观看| 99re视频这里只有精品| 久久天堂国产精品| 国产资源在线播放| 国产精品护士白丝一区av| 性欧美.com| 色影视在线观看| 自拍偷自拍亚洲精品播放| 中文字幕在线亚洲三区| 爆操欧美美女| 亚洲综合免费观看高清完整版 | 日韩va亚洲va欧美va清高| 亚洲乱码电影| 欧美激情久久久| 亚洲精品国产精品乱码| 久久欧美肥婆一二区| 国产精品久久久av久久久| 91精品国产乱码久久久久| 国模大尺度一区二区三区| 成人免费视频网站| 婷婷亚洲一区二区三区| 国产网站一区二区三区| 宅男在线精品国产免费观看| 国产中文字幕免费观看| 精品国产99久久久久久| 亚洲综合无码一区二区| 日韩欧美在线播放视频| 欧美高清免费| 亚洲精品一区二区三区影院| 疯狂揉花蒂控制高潮h| 欧美中文一区二区| 欧美成人亚洲成人| av大片免费观看| 免费看欧美女人艹b| 91青青草免费观看| 免费黄网站在线观看| 亚洲男人天堂av网| 国产综合av在线| 亚洲人成777| 亚洲国产美女久久久久| 91香蕉视频污在线观看| 亚洲三级影院| 成人欧美一区二区三区在线湿哒哒| www.好吊色| 国产欧美综合色| 国产一区二区三区小说| 草莓视频成人appios| 精品国产在天天线2019| 大吊一区二区三区| 91久久黄色| 成人av色在线观看| 日本一区高清| 亚洲一区二区高清| www.com操| 亚洲肉体裸体xxxx137| 久久综合色88| 波多野结衣家庭主妇| 成人高清av在线| 中国 免费 av| av亚洲一区二区三区| 精品成人a区在线观看| 永久av免费网站| 视频精品一区二区| 九色91在线视频| 中文字幕免费高清电视剧网站在线观看| 色婷婷综合五月| 欧美激情 亚洲| 欧美成人69| 国产在线视频欧美| 久久天堂电影| 黑人巨大精品欧美一区二区一视频 | 国产不卡视频在线播放| 亚洲一区二区不卡视频| 丝袜美腿一区| 亚洲精品综合久久中文字幕| 国产午夜免费视频| 国产精品1区2区| 黄色高清视频网站| 日韩在线你懂得| 中文字幕日韩精品在线| 青青草视频在线观看免费| 91亚洲午夜精品久久久久久| 给我免费播放片在线观看| 欧美欧美在线| 久久99久久亚洲国产| 国产免费一区二区三区免费视频| 国产精品免费免费| 亚洲第一中文av| 日韩免费久久| 成人www视频在线观看| 欧美成人精品一区二区男人看| 欧美日韩国产在线观看| 婷婷综合在线视频| 激情综合亚洲精品| 中文一区一区三区免费| 日韩毛片免费看| 久久久精品一区二区三区| 国产农村妇女毛片精品| 亚洲美女区一区| 韩国av中国字幕| 一区视频在线看| 国产日韩欧美综合精品| 日韩精品美女| 在线电影欧美日韩一区二区私密| 中文字幕在线视频第一页| 中文字幕国产精品一区二区| 亚洲人视频在线| 亚洲精品tv久久久久久久久久| 99re在线观看视频| 国产美女高潮在线| 一二美女精品欧洲| 在线免费观看av片| 亚洲精品国产精华液| 国产一卡二卡三卡四卡| 免费视频一区| 亚洲欧洲国产精品久久| 国产精一区二区| 高清欧美一区二区三区| 免费在线视频一级不卡| 欧美日韩国产一区二区三区地区| 九九视频在线免费观看| xfplay精品久久| 伊人伊成久久人综合网小说| 三区四区在线观看| 国产乱码字幕精品高清av| 亚洲熟妇无码一区二区三区| 欧美一区二区麻豆红桃视频| 91久久精品一区| 国产高清中文字幕在线| 中文字幕日韩av综合精品| 亚洲第九十九页| 91成人看片片| 久久久久久久久久一区二区三区| 91首页免费视频| 亚洲精品国产一区二区三区| 在线观看不卡| 亚洲精品视频一区二区三区| **爰片久久毛片| 国产精品成人品| 神马午夜伦理不卡| 国产一区二区黑人欧美xxxx| 亚洲精品无amm毛片| 久久精品久久综合| 丰满女人性猛交| 欧美自拍一区| 亚洲尤物视频网| 毛片免费看不卡网站| 欧美另类在线播放| 91在线播放网站| 日韩高清不卡av| 精品乱子伦一区二区| 欧美综合欧美视频| 国产精品黄色网| 亚洲精品免费播放| 午夜影院黄色片| 91丨国产丨九色丨pron| www.欧美com| 极品美女销魂一区二区三区 | 日韩亚洲视频| 欧美精品中文| 99re在线视频上| 91精品福利观看| 国产精品亚发布| 人人鲁人人莫人人爱精品| 午夜伦理精品一区| 免费在线国产视频| 美女福利视频一区| 色三级在线观看| 国产一区二区三区三区在线观看| 色综合视频在线| 精品久久久久久最新网址| 亚洲在线视频播放| 欧美性极品少妇| 男人天堂视频网| 欧美日韩中文在线| 日韩无码精品一区二区三区| 一区二区三区日本| 九九热精品视频在线播放| 囯产精品久久久久久| 日韩午夜在线观看视频| 国产人妖一区二区三区| 欧美日本在线观看| 91成年人视频| 欧美精品日韩一区| 国产精品久久久久久69| 欧美精品第1页| 97久久人国产精品婷婷| 91精品欧美久久久久久动漫 | 99热国产精品| 亚洲国产欧美视频| 99国产精品久久久久久久久久| 色哟哟无码精品一区二区三区| 成人一级视频在线观看| 国产免费一区二区三区最新6| 成人h动漫精品一区二区| 人妖粗暴刺激videos呻吟| 成人免费毛片aaaaa**| 亚洲美女在线播放| 91麻豆文化传媒在线观看| 久久精品一区二区免费播放| 久久久久九九视频| 国产精品www爽爽爽| 中文幕一区二区三区久久蜜桃| 顶级黑人搡bbw搡bbbb搡| 亚洲激情图片qvod| 久久9999久久免费精品国产| 欧美午夜丰满在线18影院| 午夜久久久久久久久久影院| 欧美日韩精品一区二区三区蜜桃| 国产尤物在线观看| 日韩大片欧美大片| 精品久久久中文| 日本免费观看视| 日韩欧美一区二区三区久久| 免费又黄又爽又猛大片午夜| 亚洲黄色片在线观看| 中国美女黄色一级片| 1024精品合集| 久草免费在线观看视频| 亚洲精品免费在线观看| 欧美另类交人妖| 日韩欧美亚洲系列| 亚洲精品在线不卡| 尤物视频在线免费观看| 日本在线不卡视频| 一区二区成人在线视频| 老牛影视av老牛影视av| 国产欧美一区二区精品性色| 国产性生活大片| 亚洲一区二区欧美激情| www.五月婷婷.com| 欧美一区二区精品久久911| 天堂网av在线播放| www日韩欧美| 亚洲码国产岛国毛片在线| 国产精品免费人成网站酒店| 亚洲午夜私人影院| 亚洲综合图片网| 日韩欧美中文免费| 秋霞av一区二区三区| 欧美日韩国产综合久久| 欧美性猛交 xxxx| 一区二区三区www| 污污在线观看| 国产精品日韩在线| 加勒比色综合久久久久久久久| 亚欧精品在线| 亚洲高清自拍| 日产精品99久久久久久| 亚洲成人短视频| 99久久精品免费看国产四区| 久久99视频| 99色这里只有精品| 久久国产麻豆精品| 北岛玲一区二区| 一区二区三区日韩欧美| 伊人色综合久久久| 国产香蕉一区二区三区在线视频 | 高潮毛片又色又爽免费| 精品国产污污免费网站入口| 蜜桃视频网站在线| 国产成人福利网站| 日日狠狠久久偷偷综合色| 国产成人一区二区三区别| 精品一区二区三区在线观看国产| 色一情一交一乱一区二区三区| 欧美日韩国产精品| 日本美女一级片| 欧美激情成人在线视频| 高清一区二区| 91xxx视频| 精品无人区卡一卡二卡三乱码免费卡 | 亚洲专区**| 欧美大片免费播放| 激情五月婷婷综合网| 任你操精品视频| 欧美日韩和欧美的一区二区| 999国产在线视频| 国产精品美女呻吟| 日本在线电影一区二区三区| 久草综合在线观看| 国产日韩欧美亚洲| 久久这里只有精品9| 国产亚洲视频在线观看| 视频二区不卡| 婷婷四月色综合| 美女精品一区二区| 国产视频精品免费| 69堂精品视频| 深夜国产在线播放| 国产在线播放一区二区| 亚洲人成高清| 国产精品无码一区二区三区免费| 欧美丝袜一区二区| 国产在线中文字幕| 国产精品日韩在线一区| 久久亚洲精品中文字幕蜜潮电影| 精品亚洲视频在线| 又紧又大又爽精品一区二区| www.五月婷| 国模私拍一区二区三区| 亚洲第一福利专区| 看欧美ab黄色大片视频免费| 国产精品久久久久久久久动漫 | 精品www久久久久奶水| 国产欧美中文在线| 国产欧美久久久精品免费| 欧美黄色免费网站| 校花撩起jk露出白色内裤国产精品| 青青草原成人网| 国产精品视频你懂的| 国产农村妇女毛片精品久久| 久久久视频免费观看| 岳的好大精品一区二区三区| 成 人 黄 色 小说网站 s色| 一区二区三区四区高清精品免费观看| 日本高清视频免费观看| 日本精品性网站在线观看| 日韩成人免费| 国产日韩视频一区| 在线观看三级视频欧美| а√天堂8资源在线官网| 国产一区二区高清不卡| 日韩avvvv在线播放| 欧产日产国产v| 亚洲乱亚洲乱妇无码| 日韩一级视频| 日本福利视频在线| 中文字幕亚洲在| 偷拍自拍在线视频| 成人在线免费观看视视频| 国产农村妇女毛片精品久久莱园子| 又色又爽的视频| 精品国产乱码久久久久久1区2区 | 国产精品日本| 青青操在线视频观看| 日韩黄在线观看| 国产区一区二| 日韩欧美黄色大片| 亚洲大尺度视频在线观看| 在线观看av的网站| 久久99导航| 国产二区国产一区在线观看| 国产一区二区视频免费| 97色在线观看| 国内精品久久久久久久影视蜜臀 | 一级全黄裸体片|