一、JVM垃圾回收機制(桌面/服務器端)
1.?核心算法:分代收集
新生代回收(Minor GC)
觸發條件:Eden區滿時觸發
算法:復制算法(Eden → Survivor區)
過程:存活對象在Survivor區間復制,年齡+1;年齡超閾值(默認15)晉升老年代
老年代回收(Major GC/Full GC)
觸發條件:老年代空間不足
算法:標記-清除(產生碎片)或標記-整理(無碎片)
耗時:10倍于Minor GC,導致應用暫停(STW)
2.?對象存活判定:可達性分析
GC Roots類型:
虛擬機棧局部變量
方法區靜態變量與常量
JNI引用對象
解決循環引用:不可達對象判定為垃圾(對比引用計數法)
3.?GC觸發場景
GC類型 | 觸發條件 | 影響范圍 |
---|---|---|
Minor GC | Eden區滿 | 僅新生代 |
Full GC | 老年代滿/調用System.gc() | 全堆+方法區 |
MetaSpace GC | 類元數據超限 | 元空間 |
二、Dalvik垃圾回收機制(Android 4.4及之前)
1.?核心設計:移動端適配
堆結構:
Zygote堆:預加載系統類(進程間共享)
Active堆:應用獨享,對象分配主區域
回收算法:標記-清除(Mark-Sweep)
位圖標記:獨立空間記錄對象狀態,減少對象頭開銷
三次STW:
每次暫停約5-10ms,導致界面卡頓
2.?致命缺陷
全堆掃描:每次GC需遍歷所有對象
內存碎片:清除后產生不連續空間,大對象分配失敗
高功耗:頻繁GC增加CPU負載
三、ART垃圾回收機制(Android 5.0+)
1.?革命性優化
2.?核心機制解析
并發標記清除(CMS):
標記階段:
初始標記(STW暫停1次):標記根對象(耗時≤1ms)
并發標記:與應用線程并行遍歷引用鏈
最終標記(非STW):處理引用變更(ModUnionTable記錄臟數據)
清除階段:后臺線程異步回收
分代策略增強:
年輕代:復制算法(Minor GC <2ms)
老年代:標記-整理(避免碎片)
大對象直存老年代:避免年輕代頻繁回收
3.?GC觸發條件
GC原因 | 觸發場景 | 線程影響 |
---|---|---|
kGcCauseForAlloc | 分配對象時內存不足 | STW暫停 |
kGcCauseBackground | 后臺并發GC(堆使用達閾值) | 無STW |
kGcCauseExplicit | 調用System.gc() | STW暫停 |
四、三大運行時GC機制對比
維度 | JVM | Dalvik | ART |
---|---|---|---|
堆結構 | 新生代+老年代+元空間 | Zygote堆+Active堆 | Image/Zygote/Allocation/Large Object Space |
回收算法 | 分代收集(復制+標記整理) | 標記-清除(全堆掃描) | CMS(并發標記+增量清除) |
STW暫停 | Full GC時顯著暫停 | 3次暫停/次GC | 僅1次初始標記暫停 |
碎片處理 | 標記整理壓縮 | 無優化(依賴Bionic) | 在線內存壓縮(ART 10+) |
移動端優化 | 無 | 寫時復制共享Zygote堆 | AOT+JIT混合編譯 |
典型GC耗時 | Full GC:100ms+ | 每次暫停5-10ms | Minor GC:<2ms;Full GC:5-10ms |
五、面試標準答案(背誦版)
Q:JVM、Dalvik與ART的GC核心區別?
A:?三者本質是不同場景的運行時環境,核心差異如下:
算法設計:
JVM:分代收集(新生代復制算法+老年代標記整理)
Dalvik:標記-清除(全堆掃描,三次STW卡頓嚴重)
ART:并發標記清除(CMS僅1次STW,增量清除減少卡頓)
堆結構:
JVM:新生代(Eden+Survivor)+老年代
Dalvik:Zygote堆(共享)+Active堆(進程獨享)
ART:四空間劃分(Image預加載類+Large Object專存大對象)
移動端優化:
Dalvik:寫時復制共享系統類(節省內存)
ART:AOT預編譯減少運行時開銷,并發GC降低STW至1次
性能指標:
卡頓:ART(5ms)< Dalvik(30ms)< JVM Full GC(100ms+)
內存利用率:ART > JVM > Dalvik(碎片問題)128
Q:ART如何實現高效GC?
A:?四大關鍵技術:
并發標記:通過ModUnionTable記錄引用變更,標記階段僅需1次STW
增量清除:回收過程與應用線程并行
堆分區:Large Object Space隔離大對象,減少年輕代壓力
AOT預編譯:安裝時生成機器碼,減少運行時解釋開銷