国产精品电影_久久视频免费_欧美日韩国产激情_成年人视频免费在线播放_日本久久亚洲电影_久久都是精品_66av99_九色精品美女在线_蜜臀a∨国产成人精品_冲田杏梨av在线_欧美精品在线一区二区三区_麻豆mv在线看

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

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

1.選擇唯一性索引

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

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

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

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

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

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

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

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

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

6.盡量使用前綴來索引

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

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

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

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

mysql會一直向右匹配直到遇到范圍查詢(>、<、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)化器會幫你優(yōu)化成索引可以識別的形式

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

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

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

比如from_unixtime(create_time) = ’2014-05-29’就不能使用到索引,原因很簡單,b+樹中存的都是數(shù)據(jù)表中的字段值,但進(jìn)行檢索時,需要把所有元素都應(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)行不斷的實踐。根據(jù)應(yīng)用的實際情況進(jìn)行分析和判斷,選擇最合適的索引方式。

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

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

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

問題

在分析前,先拋幾個問題?

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

Reference

我們先看下Reference.java中的幾個字段

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

一個Reference對象的生命周期如下:

主要分為Native層和Java層兩個部分。

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

process_discovered_references方法),然后將DiscoveredList的元素移動到PendingList中(代碼在referenceProcessor.cpp中enqueue_discovered_ref_helper方法),PendingList的隊首就是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的下一個對象 
  19.                     pending = r.discovered; 
  20.                     r.discovered = null
  21.                 } else { 
  22.                    //如果pending為null就先等待,當(dāng)有對象加入到PendingList中時,jvm會執(zhí)行notify 
  23.                     if (waitForNotify) { 
  24.                         lock.wait(); 
  25.                     } 
  26.                     // retry if waited 
  27.                     return waitForNotify; 
  28.                 } 
  29.             } 
  30.         }  
  31.         ... 
  32.  
  33.         // 如果時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類型(繼承自虛引用)的對象會有額外的處理:在其指向的對象被回收時,會調(diào)用clean方法,該方法主要是用來做對應(yīng)的資源回收,在堆外內(nèi)存DirectByteBuffer中就是用Cleaner進(jìn)行堆外內(nèi)存的回收,這也是虛引用在java中的典型應(yīng)用。

看完了Reference的實現(xiàn),再看看幾個實現(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.  

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

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

這些還得看JVM的源碼才行,因為決定對象是否需要被回收都是在GC中實現(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的處理分為幾個階段,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)存足夠時,將對應(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)不存活了,則會去調(diào)用對應(yīng)的ReferencePolicy判斷該對象是不時要被回收 
  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最后剩下的元素全是需要被回收的元素,最后會將其第一個元素賦值給上文提到過的Reference.java#pending字段。

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

其中NeverClearPolicy永遠(yuǎn)返回false,代表永遠(yuǎn)不回收SoftReference,在JVM中該類沒有被使用,AlwaysClearPolicy則永遠(yuǎn)返回true,在referenceProcessor.hpp#setup方法中中可以設(shè)置policy為AlwaysClearPolicy,至于什么時候會用到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之間的時間差。

_max_interval則代表了一個臨界值,它的值在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到底什么時候被被回收了,它和使用的策略(默認(rèn)應(yīng)該是LRUCurrentHeapPolicy),堆可用大小,該SoftReference上一次調(diào)用get方法的時間都有關(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á)后,引用字段就會被置為null,然后對象就會被回收(對于軟引用來說,如果內(nèi)存足夠的話,在Phase 1,相關(guān)的引用就會從refs_list中被移除,到Phase 3時refs_list為空集合)。

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

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

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為軟引用的時候,這里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,之后會被GC回收 
  67.       iter.clear_referent(); 
  68.     } else { 
  69.       // keep the referent around 
  70.       //標(biāo)記引用的對象為存活,該對象在這次GC將不會被回收 
  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個字段就是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á)后,引用字段就會被置為null,然后對象就會被回收(對于軟引用來說,如果內(nèi)存足夠的話,在Phase 1,相關(guān)的引用就會從refs_list中被移除,到Phase 3時refs_list為空集合)。

但對于Final references和 Phantom references,clear_referent字段傳入的是false,也就意味著被這兩種引用類型引用的對象,如果沒有其他額外處理,只要Reference對象還存活,那引用的對象是不會被回收的。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,我們看個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后會將phanRef加入到refQueue中 
  14.         Reference<? extends Object> phanRefP = refQueue.remove(); 
  15.          //這里輸出true 
  16.         System.out.println(phanRefP==phanRef); 
  17.     } 

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

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

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

End

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

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

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

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

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

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

DirectByteBuffer中是用虛引用的子類Cleaner.java來實現(xiàn)堆外內(nèi)存回收的,后續(xù)會寫篇文章來說說堆外內(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

2017-11-29 14:41:48

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

2024-06-28 09:07:19

2010-04-06 13:07:45

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

2020-07-01 17:25:28

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

2009-09-11 11:17:04

C#引用類型

2022-04-01 12:40:13

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

2009-03-06 16:48:23

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

2009-03-26 10:33:34

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

2010-03-31 17:17:32

2010-09-17 15:32:52

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

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

h片精品在线观看| 全部a∨一极品视觉盛宴| 中文字幕一区二区三区中文字幕 | 尤物视频在线免费观看| 色多多国产成人永久免费网站| 黄色片久久久久| 国产精品激情| 亚洲第一区在线| 污污的网站在线观看| 成人av片在线观看| 国产精品永久入口久久久| 日韩一级淫片| 精品国产欧美一区二区| 中文字幕av网| 久久先锋影音av鲁色资源网| 免费影院在线观看一区| 久久av导航| 久久躁狠狠躁夜夜爽| 国产91在线视频蝌蚪| 夜夜精品视频一区二区| 99久久国产综合精品五月天喷水| 日韩午夜免费视频| 国产不卡精品视男人的天堂| 亚洲电影二区| 亚洲激情国产精品| 日本成人在线播放| 欧美午夜激情小视频| 成人看片app| 99re视频精品| 天堂av免费看| 日本美女一区二区| 激情伦成人综合小说| 精品一区电影| 日本精品久久中文字幕佐佐木| 日韩欧国产精品一区综合无码| 亚洲精品美女视频| 国产第一页在线| 欧美一级欧美一级在线播放| 国内av一区二区三区| 亚洲最新视频在线观看| 99久久久无码国产精品6| 国产精品自拍在线| 亚洲bbw性色大片| 久久国产高清| 欧美日韩大片一区二区三区| 亚洲美女视频在线免费观看| 精品中文字幕一区| 亚洲区第一页| 久久大片网站| 三级精品在线观看| 尤物国产精品| 福利一区二区在线| 国产欧美高清在线| 国产精品福利电影一区二区三区四区| 亚洲乱码国产一区三区| 国产精品久久久久久久久免费樱桃| 99久久激情视频| 国产精品久久久久婷婷| 黄色av免费| 欧美网站在线观看| 麻豆网在线观看| 国产真实有声精品录音| 亚洲综合色在线| 一级片在线观看| 色先锋aa成人| www.久久ai| 永久免费精品影视网站| 成人在线免费av| 久久免费在线观看| 成人写真视频| 久久av一区二区| 国产成都精品91一区二区三| 黄色三级视频片| 精品日韩视频在线观看| 一级日本在线| 精品在线欧美视频| 成人片在线看| 国产在线xxxx| 精品国产精品网麻豆系列 | 欧美婷婷精品激情| 亚洲精品白浆高清久久久久久| 中文字幕人成人乱码| 欧美91精品久久久久国产性生爱| 成人免费毛片片v| 三上悠亚av一区二区三区| 精品国产91久久久久久| 欧美黄色视屏| 欧美激情精品久久久久久久变态| 青草国产精品| 亚洲人体一区| 中文字幕精品一区二区精品绿巨人| 粉嫩tv在线播放| 精品国产伦理网| japanese色系久久精品| 91超碰在线电影| 国产精品夜夜嗨| 91嫩草在线播放| 欧美成人一区二区三区在线观看| 成人激情久久| 国产欧美日韩一区| 不卡视频一二三| 日本免费一区二区三区最新| 精品在线小视频| 久久美女视频| www.99riav| 色婷婷久久久久swag精品| 天天免费亚洲黑人免费| 91沈先生在线观看| 91亚洲精品久久久蜜桃| 久青草国产在线| 久久国产精品久久久| 午夜久久美女| 成年人免费大片| 日韩西西人体444www| 天海翼亚洲一区二区三区| 亚洲欧洲精品在线| 婷婷久久综合九色综合伊人色| a成人v在线| 蜜桃av色综合| 17c精品麻豆一区二区免费| 国产亚洲成av人片在线观看 | 欧美人成在线观看| 91成人在线精品| 国产精品天天看天天狠| 一区二区视频国产| 亚洲444eee在线观看| 激情视频亚洲| 日韩欧美三级电影| 丁香五六月婷婷久久激情| 亚洲专区**| 国风产精品一区二区| 欧美日韩中文字幕一区| 免费av一区二区三区四区| 99热自拍偷拍| 亚洲国产日韩欧美在线动漫| 久久精品青草| 成年人黄视频网站| 欧美精品videosex极品1| 国产精品99久久久久久宅男| 欧美jizzhd欧美| 91中文字幕在线| 亚洲精品成人天堂一二三| 成人免费视频播放| 二区三区在线观看| 成人免费图片免费观看| 羞羞网站在线观看入口免费| 3d黄动漫网站| 黄视频在线观看网站| 成人高潮成人免费观看| 黄色成人在线观看| 欧美舌奴丨vk视频| 青青免费在线视频| 毛片网站在线| 国产ktv在线视频| 99久久香蕉| 忘忧草精品久久久久久久高清| 一区二区美女| 我不卡影院28| 在线成人动漫av| 玖玖玖视频精品| 久久a爱视频| 国产一区二区视频在线看| 拔插拔插海外华人免费| 男人的天堂avav| 国产h在线观看| av在线电影播放| 中文字幕一区二区三区乱码图片| 日本不卡123| 中文字幕精品一区二区精品绿巨人 | 66久久国产| 日本精品二区| 日韩欧美精品三级| 免费看黄裸体一级大秀欧美| 69xxxx欧美| 先锋在线资源一区二区三区| 亚洲成人精品久久久| 久久精品国产亚洲a| 精品肉辣文txt下载| 黄色免费观看视频网站| 97碰在线观看| 疯狂蹂躏欧美一区二区精品| 激情六月综合| 欧亚在线中文字幕免费| 久久视频这里有精品| 九九热这里只有精品6| 自拍av一区二区三区| 亚洲91中文字幕无线码三区| 国产在线激情| 欧美一级片免费播放| 97视频国产在线| 色综合久久久久综合体桃花网| 亚洲男女自偷自拍| 日韩国产激情| 成人观看视频| 国产精品一码二码三码在线| 亚洲精品一区二区三区福利| 丰满白嫩尤物一区二区| 精品freesex老太交| 2024最新电影免费在线观看| 99re在线视频免费观看|