考察點
- 垃圾回收機制,分代回收,各個代用什麼演算法
- 四大引用
JVM如何判斷一個物件例項是否應該被回收?
首先判斷物件是否存活,
1. 物件存活的判斷方法
- 引用計數器演算法
引用計數器的演算法原理:給物件新增一個引用計數器,每當有一個地方引用它時,計數器的值就會加1;當引用失效時,計數器就會減1;在任何時刻計數器的值為0的物件就是不可能再被使用的,也就是被回收的物件。
優點:效率高 缺陷:無法解決物件之間迴圈引用的問題,對於迴圈引用的物件,無法進行回收(主流JVM並未採用這種演算法)
- 可達性分析演算法(GC Roots)
在主流的JVM實現中,都是通過可達性分析演算法來判斷物件時候存活的。其基本思想:通過一系列被稱為"GC Roots"的物件作為起始點,從這些節點開始向下搜尋,搜尋走過的路徑稱為引用鏈,當一個物件到GC Roots物件沒有任何引用鏈相連,就認為GC Roots到這個物件是不可達的,判定為不可用物件,可以被回收。
Java虛擬機器將以下物件定義為 ==GC Roots== :
- Java虛擬機器棧中引用的物件,虛擬機器棧中(棧幀)
- 靜態屬性引用的物件,static物件
- 常量引用的物件,final物件
- 本地方法棧中引用的物件,nio
2.垃圾收集演算法
- 標記清除演算法
從根集合進行掃描,對存活的物件進行標記,完畢後清除未標記物件
- 複製演算法
將記憶體一分為二,每次使用一塊,將存活的物件複製到另外一塊。
時間上效率低,空間上產生記憶體碎片。
- 標記壓縮演算法
類似於標記清除,移動存活的物件向記憶體一端,然後清理邊界以外的記憶體。
- 分代收集演算法
不同的物件的生命週期(存活情況)是不一樣的,而不同生命週期的物件位於堆中不同的區域,因此對堆記憶體不同區域採用不同的策略進行回收可以提高 JVM 的執行效率。
當代商用虛擬機器使用的都是分代收集演算法:
新生代物件存活率低,就採用 複製演算法 ;老年代存活率高,就用標記清除演算法或者標記整理演算法。
Java堆記憶體一般可以分為新生代、老年代和永久代三個模組。
- 新生代:一般新建立的物件放在新生代,新生代記憶體按8:1:1分為eden和兩個survior區。大部分物件放在eden區,垃圾回收時將eden區存活的物件複製到survior0中,然後清除eden。當survior0滿了,則複製所有存活物件到survior1。然後清楚eden和survior0,再交換survior0和survior1。
- 老年代:存放生命週期較長的物件,大物件直接進入老年代(連續記憶體空間)在新生代中經歷了N次垃圾回收後仍然存活的物件就會被放到老年代中。老年代如果滿了回發生FullGC
- 永久代:用於存放靜態檔案,java類,方法。
垃圾回收有兩種型別,Minor GC 和 Full GC。
-
Minor GC:對新生代進行回收,不會影響到年老代。因為新生代的 Java 物件大多死亡頻繁,所以 Minor GC 非常頻繁,一般在這裡使用速度快、效率高的演算法,使垃圾回收能儘快完成。
-
Full GC:也叫 Major GC,對==整個堆==進行回收,包括新生代和老年代。由於Full GC需要對整個堆進行回收,所以比Minor GC要慢,因此應該儘可能減少Full GC的次數,導致Full GC的原因包括:老年代被寫滿、永久代(Perm)被寫滿和System.gc()被顯式呼叫等。
Java四大引用型別
- 強引用(StrongReference)
當記憶體空間不足,Java虛擬機器寧願丟擲OutOfMemoryError錯誤,使程式異常終止,也不會靠隨意回收具有強引用的物件來解決記憶體不足的問題。 ps:強引用其實也就是我們平時A a = new A()這個意思。
- 軟引用(SoftReference)
如果一個物件只具有軟引用,則記憶體空間足夠,垃圾回收器就不會回收它;如果記憶體空間不足了,就會回收這些物件的記憶體
- 弱引用(WeakReference)
只具有弱引用的物件擁有更短暫的生命週期。在垃圾回收器執行緒掃描它所管轄的記憶體區域的過程中,一旦發現了只具有弱引用的物件,不管當前記憶體空間足夠與否,都會回收它的記憶體。
- 虛引用(PhantomReference)(基本沒有用)
虛引用並不會決定物件的生命週期。如果一個物件僅持有虛引用,那麼它就和沒有任何引用一樣,在任何時候都可能被垃圾回收器回收。