1. 對象什么時候可以被垃圾器回收
如果一個或多個對象沒有任何的引用指向它了,那么這個對象現在就是垃圾,如果定位了垃圾,則有可能會被垃圾回收器回收
如果要定位什么是垃圾,有兩種方式來確定
- 引用計數法
- 可達性分析算法
1.1 引用計數法
1.1.1 原理
String demo = new String;
String demo = null;
1.1.2 局限
創建實例,互相調用
public class Demo {
Demo instance;
String name;
public Demo(String name)(
this.name = name;
}
Demo a = new Demo("a");
Demo b = new Demo("b?);
a.instance = b;
b.instance = a
設置a,b為null,互相引用導致ref=1,循環引用,會引發內存泄露
a = null;
b = null
1.2 可達性分析算法
現在的虛擬機采用的都是通過可達性分析算法來確定哪些內容是垃圾。
X,Y這兩個節點是可回收的
- Java 虛擬機中的垃圾回收器采用可達性分析來探索所有存活的對象
- 掃描堆中的對象,看是否能夠沿著GC Root 對象為起點的引用鏈找到該對象,找不到,表示可以回收
1.2.1 哪些對象可以作為GC Root
- 虛以機棧(棧幀中的本地變量表)中引用的對象
public static void main (Stringll args) {
Demo demo = new Demo);
demo = null;
}
- 方法區中類靜態屬性引用的對象
public static Demo a;
public static void main(Stringl] args) {
Demo b = new Demo);
b.a = new Demo;
b = null;
}
- 方法區中常量引用的對象
public static final Demo a = new Demo);
public static void main(String[] args) {
Demo demo = new Demo;
demo = null;
}
- 本地方法棧中JNI(即一般說的Native 方法)引用的對象
2. 垃圾回收算法
2.1 標記清除算法
2.1.1 原理
標記清除算法,是將垃圾回收分為2個階段,分別是標記和清除。
- 根據可達性分析算法得出的垃圾進行標記
- 對這些標記為可回收的內容進行垃圾回收
2.1.2 局限
- 優點:標記和清除速度較快
- 缺點:碎片化較為嚴重,內存不連貫的
內存分配碎片化對數組存儲不友好(數組占用連續的儲存空間,難存儲大數組)
2.2 標記整理算法
優缺點同標記清除算法,解決了標記清除算法的碎片化的問題,同時,標記壓縮算法多了一步,對象移動內存位置的步驟,其效率也有有一定的影響。
2.3 復制算法
- 優點:
- 在垃圾對象多的情況下,效率較高
- 清理后,內存無碎片
- 缺點:
- 分配的2塊內存空間,在同一個時刻,只能使用一半,內存使用率較低(一般新生代的垃圾回收器會用復制算法)
- 分配的2塊內存空間,在同一個時刻,只能使用一半,內存使用率較低(一般新生代的垃圾回收器會用復制算法)
3. JVM中的分代回收
在java8時,堆被分為了兩份:新生代和老年代(數量1:2)
對于新生代,內部又被分為了三個區域。
- 伊甸園區Eden,新生的對象都分配到這里
- 幸存者區survivor(分成from和to)
- Eden區, from區, to區(8: 1: 1)
3.1 工作機制
- 新創建的對象,都會先分配到eden區
- 當伊甸園內存不足,標記伊甸園與from(現階段沒有)的存活對象
- 將存活對象采用復制算法復制到 to 中,復制完畢后,伊甸園和 from 內存都得到釋放
- 經過一段時間后伊甸園的內存又出現不足,標記eden區域to區存活的對象,將存活的對象復制到from區
- 當幸存區對象熬過幾次回收(最多15次),晉升到老年代(幸存區內存不足或大對象會導致提前晉升)
3.2 MinorGC、 Mixed GC、 FullGC的區別
STW(Stop-The-World):暫停所有應用程序線程,等待垃圾回收的完成
- MinorGC【young GC】發生在新生代的垃圾回收,暫停時間短(STW)
- Mixed GC新生代+老年代部分區域的垃圾回收,G1 收集器特有
- FulIGC:新生代+老年代完整垃圾回收,暫停時間長(STW),應盡力避免(只有新生代和老年代內存完全不足的時候)
4. 垃圾回收器
在jvm中,實現了多種垃圾收集器,包括:
- 串行垃圾收集器
- 并行垃圾收集器
- CMS(并發)垃圾收集器
- G1垃圾收集器
4.1 串行垃圾收集器
Serial和Serial Old串行垃圾收集器,是指使用單線程進行垃圾回收,堆內存較小,適合個人電腦
- Serial 作用于新生代,采用復制算法
- Serial Old 作用于老年代,采用標記-整理算法
垃圾回收時,只有一個線程在工作,并且java應用中的所有線程都要暫停(STW),等待垃圾回收的完成。
4.2 并行垃圾收集器
Parallel New和Parallel Old是一個并行垃圾回收器,JDK8默認使用此垃圾回收器
- Parallel New作用于新生代,采用復制算法
- Parallel Old作用于老年代,采用標記-整理算法
垃圾回收時,多個線程在工作,并且java應用中的所有線程都要暫停(STW),等待垃圾回收的完成。
4.3 CMS(并發)垃圾收集器
CMS全稱 Concurrent Mark Sweep,是一款并發的、使用標記-清除算法的垃圾回收器,該回收器是針對老年代垃圾回收的,是一款以獲取最短回收停頓時間為目標的收集器,停頓時間短,用戶體驗就好。其最大特點是在進行垃圾回收時,應用仍然能正常運行。
初始標記是GC Roots連接的,并發標記是下面所有的節點。
重新標記(像檢查是否有錯題)是檢測是否有新連接的或有新垃圾節點出現。
4.4 G1垃圾收集器
應用于新生代和老年代,在JDK9之后默認使用G1
- 應用于新生代和老年代,在JDK9之后默認使用G1
- 劃分成多個區域,每個區域都可以充當 eden,survivor, old,humongous(其中 humongous 專為大對象準備)
- 采用復制算法
- 響應時間與吞吐量兼顧
- 分成三個階段:新生代回收、并發標記、混合收集
- 如果并發失敗(即回收速度趕不上創建新對象速度),會觸發 Full GC
4.4.1 Young Collection(年輕代垃圾回收)
- 隨著時間流逝,伊甸園的內存又有不足
- 將伊甸園以及之前幸存區中的存活對象,采用復制算法,復制到新的幸存區,其中較老對象晉升至老年代
4.4.2 Young Collection + Concurrent Mark(年輕代垃圾回收+并發標記)
當老年代占用內存超過閾值(默認是45%)后,觸發并發標記,這時無需暫停用戶線程
- 并發標記之后,會有重新標記階段解決漏標問題,此時需要暫停用戶線程。
- 這些都完成后就知道了老年代有哪些存活對象,隨后進入混合收集階段。此時不會對所有老年代區域進行回收,而是根據暫停時間目標優先回收價值高(存活對象少)的區域(這也是 Gabage First 名稱的由來)
4.4.3 Mixed Collection(混合垃圾回收)
混合收集階段中,參與復制的有 eden、survivor、old
復制完成,內存得到釋放。進入下一輪的新生代回收、并發標記、混合收集
5. 強引用、軟引用、弱引用、虛引用的區別
5.1 強引用
只有所有 GC Roots 對象都不通過【強引用】引用該對象,該對象才能被垃圾回收
內存溢出OOM也不會垃圾回收強引用的對象。
String user = new User();
5.2 軟引用
僅有軟引用引用該對象時,在垃圾回收后,內存仍不足時會再次出發垃圾回收
User user = new User();
SoftReference softReference = new SoftReference (user);
5.3 弱引用
僅有弱引用引用該對象時,在垃圾回收時,無論內存是否充足,都會回收弱引用對象
User user = new User);
WeakReference weakReference = new WeakReference(user);
5.4 虛引用
必須配合引用隊列使用,被引用對象回收時,會將虛引用入隊,由 Reference Handler 線程調用虛引用相關方法釋放直接內存
User user = new User();
ReferenceQueue referenceQueue = new ReferenceQueue();
PhantomReference phantomReference = new PhantomReference (user, queue);
除了釋放user對象,還要釋放虛引用對象所關聯的一些外部資源——可能是一些外部資源(不是java占用的、也不是java內存,可能是直接內存等…),這些等java對象被回收掉后再釋放。所以要把虛擬對象先記錄在引用隊列中,先記住被回收的對象,后面直接找隊列就可以了(釋放的時候有專門的線程:Reference Handler)
- 軟引用和弱引用也可以通過引用隊列釋放相關資源
5.5 區別
- 強引用:只要所有 GC Roots 能找到,就不會被回收
- 軟引用:需要配合SofiReference使用,當垃圾多次回收,內存依然不夠的時候會回收軟引用對象
- 弱引用:需要配合WeakReference使用,只要進行了垃圾回收,就會把弱引用對象回收
- 虛引用:必須配合引用隊列使用,被引用對象回收時,會將虛引用入隊,由 Reference Handler 線程調用虛引用相關方法釋放直接內存