垃圾收集主要是針對堆和方法區進行。程序計數器、虛擬機棧和本地方法棧這三個區域屬于線程私有的,只存在于線程的生命周期內,線程結束之后就會消失,因此不需要對這三個區域進行垃圾回收。
成都創新互聯公司成都網站建設按需制作,是成都網站營銷公司,為不銹鋼雕塑提供網站建設服務,有成熟的網站定制合作流程,提供網站定制設計服務:原型圖制作、網站創意設計、前端HTML5制作、后臺程序開發等。成都網站維護熱線:028-869222201. 引用計數算法
為對象添加一個引用計數器,當對象增加一個引用時計數器加 1,引用失效時計數器減 1。引用計數為 0 的對象可被回收。
在兩個對象出現循環引用的情況下,此時引用計數器永遠不為 0,導致無法對它們進行回收。正是因為循環引用的存在,因此 Java 虛擬機不使用引用計數算法。
public class Test {
public Object instance = null;
public static void main(String[] args) {
Test a = new Test();
Test b = new Test();
a.instance = b;
b.instance = a;
a = null;
b = null;
doSomething();
}
}
在上述代碼中,a 與 b 引用的對象實例互相持有了對象的引用,因此當我們把對 a 對象與 b 對象的引用去除之后,由于兩個對象還存在互相之間的引用,導致兩個 Test 對象無法被回收。
2. 可達性分析算法
通過判斷對象的引用鏈是否可達來決定對象是否可以被回收。
以 GC Roots 為起始點進行搜索,可達的對象都是存活的,不可達的對象可被回收。
Java 虛擬機使用該算法來判斷對象是否可被回收,GC Roots 一般包含以下內容:
3. 方法區的回收
因為方法區主要存放永久代對象,而永久代對象的回收率比新生代低很多,所以在方法區上進行回收性價比不高。
主要是對常量池的回收和對類的卸載。
為了避免內存溢出,在大量使用反射和動態代理的場景都需要虛擬機具備類卸載功能。
類的卸載條件很多,需要滿足以下三個條件,并且滿足了條件也不一定會被卸載:
4. finalize()
類似 C++ 的析構函數,用于關閉外部資源。但是 try-finally 等方式可以做得更好,并且該方法運行代價很高,不確定性大,無法保證各個對象的調用順序,因此最好不要使用。
當一個對象可被回收時,如果需要執行該對象的 finalize() 方法,那么就有可能在該方法中讓對象重新被引用,從而實現自救。自救只能進行一次,如果回收的對象之前調用了 finalize() 方法自救,后面回收時不會再調用該方法。
Object 的finalize()方法的作用是否與C++的析構函數作用相同?
無論是通過引用計數算法判斷對象的引用數量,還是通過可達性分析算法判斷對象是否可達,判定對象是否可被回收都與引用有關。
Java 提供了四種強度不同的引用類型。
1. 強引用
被強引用關聯的對象不會被回收。
使用 new 一個新對象的方式來創建強引用。
Object obj = new Object();
拋出OOM Error終止程序也不會回收具有強引用的對象,只有通過將對象設置為null來弱化引用,才能使其被回收。
2. 軟引用
表示對象處在有用但非必須的狀態。
被軟引用關聯的對象只有在內存不夠的情況下才會被回收。可以用來實現內存敏感的高速緩存。
使用 SoftReference 類來創建軟引用。
Object obj = new Object();
SoftReference<Object> sf = new SoftReference<Object>(obj);
obj = null; // 使對象只被軟引用關聯
3. 弱引用
表示非必須的對象,比軟引用更弱一些。適用于偶爾被使用且不影響垃圾收集的對象。
被弱引用關聯的對象一定會被回收,也就是說它只能存活到下一次垃圾回收發生之前。
使用 WeakReference 類來創建弱引用。
Object obj = new Object();
WeakReference<Object> wf = new WeakReference<Object>(obj);
obj = null;
4. 虛引用
又稱為幽靈引用或者幻影引用,一個對象是否有虛引用的存在,不會對其生存時間造成影響,也無法通過虛引用得到一個對象。
不會決定對象的生命周期,任何時候都可能被垃圾回收器回收。必須和引用隊列ReferenceQueue聯合使用。
為一個對象設置虛引用的唯一目的是能在這個對象被回收時收到一個系統通知,起哨兵作用。具體來說,就是通過判斷引用隊列ReferenceQueue是否加入虛引用來判斷被引用對象是否被GC回收。
使用 PhantomReference 來創建虛引用。
Object obj = new Object();
ReferenceQueue queue = new ReferenceQueue();
PhantomReference<Object> pf = new PhantomReference<Object>(obj, queue);
obj = null;
引用隊列(ReferenceQueue):當GC(垃圾回收線程)準備回收一個對象時,如果發現它還僅有軟引用(或弱引用,或虛引用)指向它,就會在回收該對象之前,把這個軟引用(或弱引用,或虛引用)加入到與之關聯的引用隊列(ReferenceQueue)中。如果一個軟引用(或弱引用,或虛引用)對象本身在引用隊列中,就說明該引用對象所指向的對象被回收了。無實際的存儲結構,存儲邏輯依賴于內部節點之間的關系來表達。
1. 標記 - 清除
在標記階段,從根集合進行掃描,會檢查每個對象是否為活動對象,如果是活動對象,則程序會在對象頭部打上標記。
在清除階段,會進行對象回收并取消標志位,另外,還會判斷回收后的分塊與前一個空閑分塊是否連續,若連續,會合并這兩個分塊。回收對象就是把對象作為分塊,連接到被稱為 “空閑鏈表” 的單向鏈表,之后進行分配時只需要遍歷這個空閑鏈表,就可以找到分塊。
在分配時,程序會搜索空閑鏈表尋找空間大于等于新對象大小 size 的塊 block。如果它找到的塊等于 size,會直接返回這個分塊;如果找到的塊大于 size,會將塊分割成大小為 size 與 (block - size) 的兩部分,返回大小為 size 的分塊,并把大小為 (block - size) 的塊返回給空閑鏈表。
不足:
2. 標記 - 整理
讓所有存活的對象都向一端移動,然后直接清理掉端邊界以外的內存。
優點:
不足:
3. 復制
將內存劃分為大小相等的兩塊,每次只使用其中一塊,當這一塊內存用完了就將還存活的對象復制到另一塊上面,然后再把使用過的內存空間進行一次清理。
主要不足是只使用了內存的一半。
現在的商業虛擬機都采用這種收集算法回收新生代,但是并不是劃分為大小相等的兩塊,而是一塊較大的 Eden 空間和兩塊較小的 Survivor 空間,每次使用 Eden 和其中一塊 Survivor。在回收時,將 Eden 和 Survivor 中還存活著的對象全部復制到另一塊 Survivor 上,最后清理 Eden 和使用過的那一塊 Survivor。
HotSpot 虛擬機的 Eden 和 Survivor 大小比例默認為 8:1,保證了內存的利用率達到 90%。如果每次回收有多于 10% 的對象存活,那么一塊 Survivor 就不夠用了,此時需要依賴于老年代進行空間分配擔保,也就是借用老年代的空間存儲放不下的對象。
4. 分代收集
Stop-the-World
Safepoint
分析過程中對象引用關系不會發生變化的點;
產生Safepoint的地方:方法調用;循環跳轉;異常跳轉等
現在的商業虛擬機采用分代收集算法,它根據對象存活周期將內存劃分為幾塊,不同塊采用適當的收集算法。
一般將堆分為新生代和老年代。
以上是 HotSpot 虛擬機中的 7 個垃圾收集器,連線表示垃圾收集器可以配合使用。
1. Serial 收集器(-XX:+UseSerialGC)
Serial 翻譯為串行,也就是說它以串行的方式執行。
它是單線程的收集器,只會使用一個線程進行垃圾收集工作。
它的優點是簡單高效,在單個 CPU 環境下,由于沒有線程交互的開銷,因此擁有最高的單線程收集效率。
它是 Client 場景下的默認新生代收集器,因為在該場景下內存一般來說不會很大。它收集一兩百兆垃圾的停頓時間可以控制在一百多毫秒以內,只要不是太頻繁,這點停頓時間是可以接受的。
2. ParNew 收集器(-XX:+UseParNewGC)
它是 Serial 收集器的多線程版本。
它是 Server 場景下默認的新生代收集器,除了性能原因外,主要是因為除了 Serial 收集器,只有它能與 CMS 收集器配合使用。
3. Parallel Scavenge 收集器(-XX:+UseParallelGC)
與 ParNew 一樣是多線程收集器。
其它收集器目標是盡可能縮短垃圾收集時用戶線程的停頓時間,而它的目標是達到一個可控制的吞吐量,因此它被稱為“吞吐量優先”收集器。這里的吞吐量指 CPU 用于運行用戶程序的時間占總時間的比值。
停頓時間越短就越適合需要與用戶交互的程序,良好的響應速度能提升用戶體驗。而高吞吐量則可以高效率地利用 CPU 時間,盡快完成程序的運算任務,適合在后臺運算而不需要太多交互的任務。
縮短停頓時間是以犧牲吞吐量和新生代空間來換取的:新生代空間變小,垃圾回收變得頻繁,導致吞吐量下降。
可以通過一個開關參數打開 GC 自適應的調節策略(GC Ergonomics),就不需要手工指定新生代的大小(-Xmn)、Eden 和 Survivor 區的比例、晉升老年代對象年齡等細節參數了。虛擬機會根據當前系統的運行情況收集性能監控信息,動態調整這些參數以提供最合適的停頓時間或者大的吞吐量。
4. Serial Old 收集器(-XX:+UseSerialOldGC)
是 Serial 收集器的老年代版本,也是給 Client 場景下的虛擬機使用。如果用在 Server 場景下,它有兩大用途:
5. Parallel Old 收集器(-XX:+UseParallelOldGC)
是 Parallel Scavenge 收集器的老年代版本。
在注重吞吐量以及 CPU 資源敏感的場合,都可以優先考慮 Parallel Scavenge 加 Parallel Old 收集器。
6. CMS 收集器(-XX:+UseConcMarkSweepGC)
CMS(Concurrent Mark Sweep),Mark Sweep 指的是標記 - 清除算法。
分為以下六個流程:
具有以下缺點:
7. G1 收集器(-XX:+UseG1GC)
G1(Garbage-First),它是一款面向服務端應用的垃圾收集器,在多 CPU 和大內存的場景下有很好的性能。HotSpot 開發團隊賦予它的使命是未來可以替換掉 CMS 收集器。
堆被分為新生代和老年代,其它收集器進行收集的范圍都是整個新生代或者老年代,而 G1 可以直接對新生代和老年代一起回收。
G1 把堆劃分成多個大小相等的獨立區域(Region),新生代和老年代不再物理隔離。
通過引入 Region 的概念,從而將原來的一整塊內存空間劃分成多個的小空間,使得每個小空間可以單獨進行垃圾回收。這種劃分方法帶來了很大的靈活性,使得可預測的停頓時間模型成為可能。通過記錄每個 Region 垃圾回收時間以及回收所獲得的空間(這兩個值是通過過去回收的經驗獲得),并維護一個優先列表,每次根據允許的收集時間,優先回收價值大的 Region。
每個 Region 都有一個 Remembered Set,用來記錄該 Region 對象的引用對象所在的 Region。通過使用 Remembered Set,在做可達性分析的時候就可以避免全堆掃描。
如果不計算維護 Remembered Set 的操作,G1 收集器的運作大致可劃分為以下幾個步驟:
具備如下特點:
1. 使用 CMS 收集器
CMS 收集器進行垃圾回收,有 4 個步驟:
其中初始標記和重新標記需要 “stop the world”,但耗時時間最長的并發標記、并發清除過程中,GC 線程都可與用戶線程一起工作。整體上說,CMS 和用戶線程是并行的。
2. 增量算法
基本思路:若一次性將所有垃圾進行處理,會造成系統長時間的停頓,則就讓 GC 線程與用戶線程交替執行。每次 GC 線程只收集一小塊區域的內存空間,接著切換到用戶線程,重復幾次,直至 GC 完成。
問題:存在線程切換和上下文切換,造成系統吞吐量下降。
名稱欄目:詳解Java虛擬機(第⑤篇)——垃圾收集-創新互聯
新聞來源:http://vcdvsql.cn/article14/epdde.html
成都網站建設公司_創新互聯,為您提供微信小程序、虛擬主機、網站排名、微信公眾號、靜態網站、自適應網站
聲明:本網站發布的內容(圖片、視頻和文字)以用戶投稿、用戶轉載內容為主,如果涉及侵權請盡快告知,我們將會在第一時間刪除。文章觀點不代表本網站立場,如需處理請聯系客服。電話:028-86922220;郵箱:631063699@qq.com。內容未經允許不得轉載,或轉載時需注明來源: 創新互聯