首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
您现在的位置: 程式碼之家 >> 編程語言 >  >> Java 程式設計 >> Java 相關知識

Java/Android 記憶體洩漏成因全景分析與防禦策略

     編輯:Java 相關知識

JVM 記憶體管理基礎

Java 記憶體分為三大區域:

  • 方法區/靜態區:類資訊、常量、static 變數,隨類載入/卸載
  • 堆區:new 出來的物件實例、陣列,GC 主戰場
  • 棧區:方法執行時的局部變數(基本型別、物件參考),隨方法結束自動釋放

GC 採用 可達性分析:從 GC Roots(執行緒棧局部變數、靜態屬性、常量、JNI 參考)出發,不可達物件視為垃圾回收。記憶體洩漏本質:物件仍可達但程式不再使用,導致 GC 無法回收,長期佔用堆空間。

六大經典洩漏場景

1. 靜態集合持有物件

static Vector v = new Vector();
for (int i=0; i<100; i++) {
    Object o = new Object();
    v.add(o);
    o = null; // 僅切斷局部參考,Vector 仍持有
}

對策:用完後 v.clear() 或 v = null;避免長生命週期集合累積短生命物件。

2. 集合元素修改後 remove 失效

HashSet 依賴 hashCode()/equals();修改參與雜湊計算的欄位後,remove() 找不到原位置,導致物件殘留。

對策:不可變物件作 Key/Element;或先 remove 再修改再 add。

3. 監聽器/回調未註銷

addXXXListener() 後未對應 removeXXXListener(),被觀察者持有觀察者參考,阻礙回收。

4. 資源連線未關閉

  • JDBC:Connection、Statement、ResultSet 必須在 finally 顯式 close()
  • IO:InputStream/OutputStream/Socket 同理
  • 連線池場景:歸還連線 ≠ 關閉物理連線,仍需關閉 Statement/ResultSet

5. 內部類/匿名類隱性持有外部類

  • 非靜態內部類實例隱含 OuterClass.this 參考
  • 單例持有 Activity/Context 導致無法釋放(Android 常見)
  • Handler 非靜態內部類 + 延遲訊息 → Activity 洩漏

對策:static 內部類 + WeakReference<Outer>;onDestroy() 移除訊息 handler.removeCallbacksAndMessages(null)。

6. 單例模式濫用

單例生命週期 = 應用進程;若持有短生命物件參考 → 洩漏。Android 中傳入 Application Context 而非 Activity Context。

Android 專項防洩清單

  • Bitmap:不用時先 recycle() 再 null(釋放 Native 記憶體)
  • Adapter:使用 ViewHolder 復用 convertView
  • 靜態變數:慎用;必用則 lazy init + 適時清空
  • 四大組件:BroadcastReceiver、ContentObserver、Cursor、File、Stream 必須在對應生命週期註銷/關閉
  • LeakCanary:接入自動偵測洩漏

參考類型選擇指南

類型回收時機適用場景
Strong永不主動回收核心業務物件
SoftReference記憶體不足前圖片快取、大物件緩衝
WeakReference下次 GC 時監聽器、快取、非關鍵引用
PhantomReference最終化前精確追蹤回收、DirectBuffer 清理

「誰創建、誰釋放」,保持對物件生命週期敏感,是根治記憶體洩漏的核心心法。

  1. 上一篇文章:
  2. 下一篇文章:
Copyright © 程式碼之家 All Rights Reserved