一区二区三区在线-一区二区三区亚洲视频-一区二区三区亚洲-一区二区三区午夜-一区二区三区四区在线视频-一区二区三区四区在线免费观看

服務器之家:專注于服務器技術及軟件下載分享
分類導航

PHP教程|ASP.NET教程|Java教程|ASP教程|編程技術|正則表達式|C/C++|IOS|C#|Swift|Android|VB|R語言|JavaScript|易語言|vb.net|

服務器之家 - 編程語言 - Java教程 - 面試中經常被問到Java引用類型原理,帶你深入剖析

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

2022-01-12 23:24Java456 Java教程

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

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

1.選擇唯一性索引

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

2.為經常需要排序、分組和聯合操作的字段建立索引

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

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

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

4.限制索引的數目

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

5.盡量使用數據量少的索引

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

6.盡量使用前綴來索引

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

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

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

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的順序可以任意調整。

9.=和in可以亂序。

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

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

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

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

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

12.盡量的擴展索引,不要新建索引。

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

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

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

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

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

問題

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

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

Reference

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

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

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

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

主要分為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就先等待,當有對象加入到PendingList中時,jvm會執行notify 
  23.                     if (waitForNotify) { 
  24.                         lock.wait(); 
  25.                     } 
  26.                     // retry if waited 
  27.                     return waitForNotify; 
  28.                 } 
  29.             } 
  30.         }  
  31.         ... 
  32.  
  33.         // 如果時CLeaner對象,則調用clean方法進行資源回收 
  34.         if (c != null) { 
  35.             c.clean(); 
  36.             return true
  37.         } 
  38.         //將Reference加入到ReferenceQueue,開發者可以通過從ReferenceQueue中poll元素感知到對象被回收的事件。 
  39.         ReferenceQueue<? super Object> q = r.queue; 
  40.         if (q != ReferenceQueue.NULL) q.enqueue(r); 
  41.         return true
  42.  } 

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

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

看完了Reference的實現,再看看幾個實現類里,各自有什么不同。

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.  

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

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

這些還得看JVM的源碼才行,因為決定對象是否需要被回收都是在GC中實現的。

  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. //該階段的主要目的就是當內存足夠時,將對應的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.     //如果引用的對象已經不存活了,則會去調用對應的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發現的某種引用類型(虛引用、軟引用、弱引用等),而

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

ReferencePolicy一共有4種實現:NeverClearPolicy,AlwaysClearPolicy,LRUCurrentHeapPolicy,LRUMaxHeapPolicy。

其中NeverClearPolicy永遠返回false,代表永遠不回收SoftReference,在JVM中該類沒有被使用,AlwaysClearPolicy則永遠返回true,在referenceProcessor.hpp#setup方法中中可以設置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的靜態字段clock,

java_lang_ref_SoftReference::timestamp(p)對應是字段timestamp。如果上次GC后有調用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到底什么時候被被回收了,它和使用的策略(默認應該是LRUCurrentHeapPolicy),堆可用大小,該SoftReference上一次調用get方法的時間都有關系。

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,這也符合我們的預期:對象不可達后,引用字段就會被置為null,然后對象就會被回收(對于軟引用來說,如果內存足夠的話,在Phase 1,相關的引用就會從refs_list中被移除,到Phase 3時refs_list為空集合)。

但對于Final references和 Phantom references,clear_referent字段傳入的是false,也就意味著被這兩種引用類型引用的對象,如果沒有其他額外處理,只要Reference對象還存活,那引用的對象是不會被回收的。Final references和對象是否重寫了finalize方法有關,不在本文分析范圍之內,我們接下來看看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.   // 移除所有指向對象還存活的引用 
  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.   // 根據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.       //標記引用的對象為存活,該對象在這次GC將不會被回收 
  71.       iter.make_referent_alive(); 
  72.     } 
  73.     ... 
  74.   } 
  75.     ... 

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

  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,這也符合我們的預期:對象不可達后,引用字段就會被置為null,然后對象就會被回收(對于軟引用來說,如果內存足夠的話,在Phase 1,相關的引用就會從refs_list中被移除,到Phase 3時refs_list為空集合)。

但對于Final references和 Phantom references,clear_referent字段傳入的是false,也就意味著被這兩種引用類型引用的對象,如果沒有其他額外處理,只要Reference對象還存活,那引用的對象是不會被回收的。Final references和對象是否重寫了finalize方法有關,不在本文分析范圍之內,我們接下來看看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方法永遠返回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.     } 

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

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

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

End

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

1.我們經常在網上看到軟引用的介紹是:在內存不足的時候才會回收,那內存不足是怎么定義的?為什么才叫內存不足?

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

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

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

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

DirectByteBuffer中是用虛引用的子類Cleaner.java來實現堆外內存回收的,后續會寫篇文章來說說堆外內存的里里外外。

原文地址:https://www.toutiao.com/a7051934204413886990/

延伸 · 閱讀

精彩推薦
  • Java教程升級IDEA后Lombok不能使用的解決方法

    升級IDEA后Lombok不能使用的解決方法

    最近看到提示IDEA提示升級,尋思已經有好久沒有升過級了。升級完畢重啟之后,突然發現好多錯誤,本文就來介紹一下如何解決,感興趣的可以了解一下...

    程序猿DD9332021-10-08
  • Java教程Java使用SAX解析xml的示例

    Java使用SAX解析xml的示例

    這篇文章主要介紹了Java使用SAX解析xml的示例,幫助大家更好的理解和學習使用Java,感興趣的朋友可以了解下...

    大行者10067412021-08-30
  • Java教程Java8中Stream使用的一個注意事項

    Java8中Stream使用的一個注意事項

    最近在工作中發現了對于集合操作轉換的神器,java8新特性 stream,但在使用中遇到了一個非常重要的注意點,所以這篇文章主要給大家介紹了關于Java8中S...

    阿杜7482021-02-04
  • Java教程Java實現搶紅包功能

    Java實現搶紅包功能

    這篇文章主要為大家詳細介紹了Java實現搶紅包功能,采用多線程模擬多人同時搶紅包,文中示例代碼介紹的非常詳細,具有一定的參考價值,感興趣的小伙...

    littleschemer13532021-05-16
  • Java教程20個非常實用的Java程序代碼片段

    20個非常實用的Java程序代碼片段

    這篇文章主要為大家分享了20個非常實用的Java程序片段,對java開發項目有所幫助,感興趣的小伙伴們可以參考一下 ...

    lijiao5352020-04-06
  • Java教程小米推送Java代碼

    小米推送Java代碼

    今天小編就為大家分享一篇關于小米推送Java代碼,小編覺得內容挺不錯的,現在分享給大家,具有很好的參考價值,需要的朋友一起跟隨小編來看看吧...

    富貴穩中求8032021-07-12
  • Java教程Java BufferWriter寫文件寫不進去或缺失數據的解決

    Java BufferWriter寫文件寫不進去或缺失數據的解決

    這篇文章主要介紹了Java BufferWriter寫文件寫不進去或缺失數據的解決方案,具有很好的參考價值,希望對大家有所幫助。如有錯誤或未考慮完全的地方,望...

    spcoder14552021-10-18
  • Java教程xml與Java對象的轉換詳解

    xml與Java對象的轉換詳解

    這篇文章主要介紹了xml與Java對象的轉換詳解的相關資料,需要的朋友可以參考下...

    Java教程網2942020-09-17
主站蜘蛛池模板: 日韩在线观看网站 | 视频一区二区三区在线 | 视频国产91 | 无码人妻丰满熟妇啪啪网不卡 | 日本高清在线精品一区二区三区 | bdsm酷刑折磨死美女 | 污软件在线观看 | 激情另类国内一区二区视频 | 国产精品一级香蕉一区 | 亚洲欧美日韩中文字幕久久 | 国产成人福利免费视频 | 欧美整片完整片视频在线 | 青草青草伊人精品视频 | 日韩中文在线 | 国产成人免费 | 全肉一女n男np高h乳 | 国产福利一区二区在线精品 | 国色天香社区在线视频免费观看 | 国产欧美日韩高清专区ho | 日韩视频在线免费 | 无人区在线观看免费观看 | 99久久久久国产 | 国产99视频精品免费视频7 | 人与动人物人a级特片 | 国产午夜不卡 | 青草香蕉精品视频在线观看 | 三级小说网站 | aaaa黄 | 亚洲a视频在线 | 国产99热 | 欧美美女被艹 | 婷婷综合久久 | 日韩视频在线精品视频免费观看 | 亚洲国产成人精品无码区5566 | 日本公与妇中文在线 | 99久久精品免费看国产 | 99国产情在线视频 | 亚洲 欧美 中文字幕 在线 | 国产精品最新资源网 | 婷婷福利 | 美女乳液 |