作者 | 慕魚
來源 | 淩雲時刻(微信号:linuxpk)
WHY:為什麼要回收?
Java虛拟機的記憶體資源有限,在不斷運作配置設定記憶體過程中,如果沒有垃圾回收機制,會逐漸消耗并最終導緻記憶體資源不足。Java 虛拟機需要回收的垃圾,就是那些不再被使用的記憶體。
ECS雲伺服器中主控端(host)的計算資源有限,在不斷為雲服務分類資源過程中,如果沒有資源回收與釋放,同樣會導緻資源消耗完。ECS雲伺服器中,主控端需要回收的資源包含雲伺服器運作占用的所有資源,如記憶體、vCPU、IP位址、磁盤存儲空間、網絡帶寬、GPU、FPGA等資源。
WHAT:回收什麼?
- Java虛拟機垃圾回收
如何确定記憶體不再被使用了呢?
- 引用計數法
這個算法的實作是,給對象中添加一個引用計數器,每當一個地方引用這個對象時,計數器值+1;當引用失效時,計數器值-1。任何時刻計數值為0的對象就是不可能再被使用的。這種算法使用場景很多,但是,Java中卻沒有使用這種算法,因為這種算法很難解決對象之間互相引用的情況。
- 可達性分析法
這個算法的基本思想是通過一系列稱為“GC Roots”的對象作為起始點,從這些節點向下搜尋,搜尋所走過的路徑稱為引用鍊,當一個對象到GC Roots沒有任何引用鍊(即GC Roots到對象不可達)時,則證明此對象是不可用的。
那麼問題又來了,如何選取GCRoots對象呢?
在Java語言中,可以作為GCRoots的對象包括下面幾種:
- 虛拟機棧(棧幀中的局部變量區,也叫做局部變量表)中引用的對象
- 方法區中的類靜态屬性引用的對象
- 方法區中常量引用的對象
- 本地方法棧中JNI(Native方法)引用的對象

由圖可知,obj8、obj9、obj10都沒有到GCRoots對象的引用鍊,即便obj9和obj10之間有引用鍊,他們還是會被當成垃圾處理,可以進行回收。
- ECS資源整理
目前業務允許回收的場景包含以下三點:
- 使用者主動釋放的資源,或者使用者按量收費停機資源【按量收費與否不保證庫存預留,是以資源可被回收】
- 使用者購買的競價執行個體,當資源緊張的時候,可以回收資源給其他類型産品使用。
- 使用者購買的預付費産品,欠費一定時間後,系統便可以對該ECS産品進行資源回收。
HOW:怎麼回收?
在确定了哪些垃圾可以被回收後,垃圾收集器要做的事情就是開始進行垃圾回收,但是這裡面涉及到一個問題是:如何高效地進行垃圾回收?
由于Java虛拟機規範并沒有對如何實作垃圾收集器做出明确的規定,是以各個廠商的虛拟機可以采用不同的方式來實作垃圾收集器。
- Mark-Sweep(标記—清除)算法
分為“标記”和“清除”兩個階段:首先标記出所有需要回收的對象,标記完成後統一回收所有被标記的對象。這種算法的不足主要展現在效率和空間,從效率的角度講,标記和清除兩個過程的效率都不高;從空間的角度講,标記清除後會産生大量不連續的記憶體碎片, 記憶體碎片太多可能會導緻以後程式運作過程中在需要配置設定較大對象時,無法找到足夠的連續記憶體而不得不提前觸發一次垃圾收集動作。
- Copying(複制)算法
為了解決Mark-Sweep算法的缺陷,Copying算法就被提了出來。它将可用記憶體按容量劃分為大小相等的兩塊,每次隻使用其中的一塊。當這一塊的記憶體用完了,就将還存活着的對象複制到另外一塊上面,然後再把已使用的記憶體空間一次清理掉,這樣一來就不容易出現記憶體碎片的問題。這種算法雖然實作簡單,運作高效且不容易産生記憶體碎片,但是卻對記憶體空間的使用做出了高昂的代價,因為能夠使用的記憶體縮減到原來的一半。
- Mark-Compact(标記—整理)算法
為了解決Copying算法的缺陷,充分利用記憶體空間,提出了Mark-Compact算法。該算法标記階段和Mark-Sweep一樣,但是在完成标記之後,它不是直接清理可回收對象,而是将存活對象都向一端移動,然後清理掉端邊界以外的記憶體。
- Generational Collection(分代收集)算法
現代商用虛拟機基本都采用分代收集算法來進行垃圾回收。這種算法沒什麼特别的,無非是上面内容的結合罷了,根據對象的生命周期的不同将記憶體劃分為幾塊,然後根據各塊的特點采用最适當的收集算法。大批對象死去、少量對象存活的(新生代),使用複制算法,複制成本低;對象存活率高、沒有額外空間進行配置設定擔保的(老年代),采用标記—清理算法或者标記—整理算法。
這種算法是ECS最開始回收的雛形,使用者送出釋放請求或者滿足釋放調節的ECS伺服器,則進行實時的資源回收與釋放。顯而易見,這種算法會導緻主控端存在大量的碎片。當大規格執行個體建立時,導緻無法找到足夠資源的主控端。
借鑒Java記憶體對應回收的方式,ECS整理技術經曆了兩個裡程碑的突破。
第一裡程碑:對于需要進行資源整理的執行個體進行标記,等待啟動或重新開機遷移,即使用者在啟動或重新開機伺服器的時候,ECS管控系統會将執行個體重新排程到更合适的主控端上。
第二裡程碑:熱遷移 這一劃時代功能上線後,執行個體理論上可以在任何時間,進行主控端切換。而不必局限于啟動或重新開機時機。是以,可以根據ECS雲伺服器的負載,功耗,碎片等名額進行實時整理。
将叢集的中的若幹主控端根據其生産雲伺服器生命周期的長度進行分組,分組的個數、組内的數量可以根據每個叢集的不同情況進行設定。
如圖所示,可以将主控端分為三組,分别為小時組、按天組和按月組,數量分别為n、k、i個。根據不同的叢集使用情況,可以增加分鐘組、按年組等等。每個組内主控端的數量,根據生産雲伺服器不同需要可以動态擴容、縮減或者轉換。n、k、i的數量也是可以動态變化和調整的。
- 使用者發起建立雲伺服器請求,服務管理控制中心擷取使用者建立請求,例如包月、包年、包周、按量等類型,根據評估算法,将其轉化為生命周期預估因子。生命周期預估因子傳遞給主控端分組管理系統進行解析,得到對應時長的主控端分組。進而配置設定主控端的位址,最後,交給主控端進行生産。
- 使用者發起的釋放請求,直接由實體伺服器進行釋放。
- 主控端分組管理系統會檢查組内每個主控端上的雲伺服器,如果雲伺服器存活時長滿足遷移條件,如果不滿足,則進行雲伺服器生命周期因子疊加,如果滿足則進行雲伺服器遷移。
- 雲伺服器遷移分為兩類,滿足組間間遷移條件,則直接進行跨組的遷移,否則進行組内主控端排程遷移。
總結
本文簡單談了一下Java垃圾回收與ECS資源整理算法的比較,兩者有很多相通的解決算法。但實際上,ECS資源整理需要考慮的情況比本文談到的更複雜,比如:
- 雲伺服器的負載情況
- 雲伺服器的聚合與打散情況
- 主控端的健康狀況
- 主控端機架的功耗情況
- 主控端機型
- 網絡及磁盤負載情況
排程系統的整理排程優化是一個龐大複雜又具有劃時代意義的課題,ECS控制系統正在多方面合作不斷優化這一問題,相信未來的ECS管控排程系統會像“後羿射日”般精準!
參考文獻:ava垃圾回收(GC)機制詳解