1.如何判斷對象已死
1.1 對象引用的4種類型(強軟弱虛)
1.1.1 強引用
特點:最常見的引用類型,只要強引用存在,對象絕不會被回收
Object strongObj = new Object(); // 強引用
注意:集合類型,如果對象不再使用,應當及時清除,避免內存泄漏
1.1.2 軟引用
特點:內存不足時才會被回收,適合實現緩存
SoftReference<byte[]> softRef = new SoftReference<>(new byte[1024*1024*10]); // 10MB
?適用場景:
- 緩存(EhCache中使用軟引用,一旦觸發內存不足時,就會由JVM自動清理數據,釋放內存)
緩存本身的含義:可刪除
1.1.3 弱引用
特點:下次GC時必定被回收,適合維護非必要數據
// 弱引用基本示例
WeakReference<String> weakRef = new WeakReference<>(new String("Weak Object"));
?適用場景:
? ? ? ? 臨時的數據
1.1.4 虛引用
1.1.5 其他
1.1.5.1 ReferenceQueue
referenceQueue(引用隊列)是Java引用類型體系中的關鍵組件,與SoftReference、WeakReference和PhantomReference配合使用,提供了對象生命周期監控的機制。
- ?通知機制:當被引用的對象達到相應回收狀態時,引用對象本身會進入隊列
- ??非阻塞設計:提供poll()和remove()兩種獲取方式
- ?GC協作:由垃圾收集器在適當時候自動入隊
?工作流程
?創建引用對象 → 關聯ReferenceQueue → 對象被回收 → 引用對象入隊 → 客戶端處理
引用類型 | 入隊時機 | 典型用途 |
---|---|---|
虛引用 | 對象被回收且finalize完成后 | 精準資源清理 |
弱引用 | 對象被GC回收時 | 緩存/臨時映射 |
軟引用 | 內存不足被回收時 | 內存敏感緩存 |
強引用 | 從不入隊 | 常規對象引用 |
?
1.1.6 使用案例
軟引用緩存,清除無效的key
/*** 弱引用緩存*/public class WeakCache<K, V> {private final ConcurrentHashMap<K, WeakReference<V>> cache = new ConcurrentHashMap<>();private final ReferenceQueue<V> queue = new ReferenceQueue<>();public void put(K key, V value) {cleanUp();cache.put(key, new WeakReference<>(value, queue));}// 清除緩存中已釋放的引用(原因:value是弱引用、但是key是強引用,value被釋放了,key沒有作用,應當也被釋放)private void cleanUp() {Reference<? extends V> ref;while ((ref = queue.poll()) != null) {Reference<? extends V> finalRef = ref;cache.values().removeIf(weakRef -> weakRef == finalRef);}}
}
1.2 判斷對象已死
1.2.1 方案1:引用計數算法
核心思想
- 每個對象維護一個引用計數器,記錄有多少引用指向它
計數規則
- 新引用指向對象時,計數器+1
- 引用失效時,計數器-1
- 計數器=0時立即回收對象
1.2.2 方案2:可達性分析算法
核心思想
- 通過GC Roots作為起點,遍歷對象引用鏈
判定規則
- 從GC Roots不可達的對象判定為垃圾
- 通常在特定時間點(如堆空間不足時)執行
GC Roots有哪些
GC Roots是垃圾回收的起點,所有從這些根對象直接或間接可達的對象都被視為存活對象。以下是Java中常見的GC Roots類型:
- 虛擬機棧(棧幀中的本地變量表)中的引用對象
public void method() {Object localObj = new Object(); // localObj是GC Root// 方法執行期間,localObj引用的對象不會被回收
}當前所有正在執行的方法中的局部變量和參數包括Java方法、native方法的棧幀中的引用
- ???方法區中類靜態屬性引用的對象
class MyClass {static Object staticObj = new Object(); // staticObj是GC Root
}特點:類的靜態變量引用的對象直到ClassLoader卸載前都會保持強引用
- 方法區中常量引用的對象
class MyClass {static final String CONSTANT = "常量"; // CONSTANT是GC Root
}特點:包括字符串常量池中的引用編譯期常量(constantValue屬性)不算真正的GC Root
- 本地方法棧中JNI(Java Native Interface)引用的對象
public native void nativeMethod(Object obj); // native代碼中的obj引用Java調用native方法時傳入的參數對象
全局JNI引用(NewGlobalRef創建)也是GC Root
- 被同步鎖(synchronized)持有的對象
- Java虛擬機內部的引用
包括:
基本類型對應的Class對象
常駐異常對象(NullPointerException等)
系統類加載器
類加載器管理的Class對象
1.2.3 工作原理對比
特征 | 引用計數 | 可達性分析 |
---|---|---|
觸發時機 | 實時(引用變更時) | 周期性/按需 |
執行速度 | 分散的微小停頓 | 集中式停頓(STW問題) |
內存回收 | 立即回收 | 延遲回收 |
實現復雜度 | 簡單 | 復雜 |
對象判定 | 計數器=0 | GC Roots不可達 |
?
1.2.4 優缺點分析
1.2.4.1 引用計數的優缺點
優點:
- 立即回收內存,減少內存占用
- 垃圾回收開銷平均分配到程序運行中
- 不需要STW(Stop-The-World)暫停
缺點:
- 計數器維護帶來額外內存開銷
- 頻繁更新計數器影響性能
- 循環引用問題(主要缺陷)
// 引用計數無法處理的場景
class A { B b; }
class B { A a; }void createCycle() {A a = new A(); // A計數=1B b = new B(); // B計數=1a.b = b; // B計數=2b.a = a; // A計數=2// 即使外部引用消失...a = null; // A計數=1b = null; // B計數=1// 對象仍無法回收!
}// 可達性分析可以正確處理
1.2.4.2 可達性分析的優缺點
優點:
- 能正確處理循環引用
- 回收效率高(集中處理)
- 現代JVM采用的成熟方案
缺點:
- 需要STW暫停應用線程
- 實現復雜度高(需要準確識別GC Roots)
- 內存回收不及時
java采用可達性分析算法
2.垃圾收集算法
分代收集理論
標記-清除算法
標記-復制算法
標記-整理算法
算法細節(根節點枚舉、安全點、安全區域、記憶集與卡表、寫屏障、并發的可達性分析)
3.常見的垃圾收集器
3.1 serial
3.2 parNew
3.3 Parallel Scavenge
3.4 serial old
3.5 Parallel Old
3.6 CMS
3.7 G1
3.8 ZGC
4. 內存分配策略
對象優先分配到Eden區
大對象直接進入老年代
長期存活的對象將進入老年代
動態對象年齡判斷
空間分配擔保
垃圾回收的優化案例
fullGc問題
對于絕對不可接受Full GC的系統:
- 商用解決方案:Azul Zing(C4 GC)、IBM Balanced GC
- 內存架構改造:
- 堆外內存存儲(HBase/OFF-Heap)
- 分布式緩存集群
- 服務拆分:將內存敏感組件獨立部署
最佳實踐:某證券交易所系統通過G1+堆外緩存方案,實現連續18個月零Full GC,老年代內存通過并發標記和定期混合回收保持85%以上可用率。