【安卓筆記】OOM與內存優化

0. 環境:

電腦:Windows10

Android Studio: 2024.3.2

編程語言: Java

Gradle version:8.11.1

Compile Sdk Version:35

Java 版本:Java11

1.什么是OOM

OOM即 OutOfMemoryError 內存溢出錯誤。常見于一些

  • 資源型對象未關閉
  • 注冊對象未注銷(反注冊)
  • 類的靜態變量持有大量數據對象
  • 單例造成的內存泄漏
  • 非靜態內部類的靜態實例
  • Handler臨時性內存泄漏
  • 容器中的對象沒清理造成的內存泄漏
  • WebView
  • 使用ListView時造成的內存泄漏

等導致的。


Android內存泄漏常見場景及解決方案

  • 資源性對象未關閉

????????常見場景:例如:Bitmap,使用后就不管了。

????????解決方案:對于資源性對象不再使用時,應該立即close(),然后設置為null。例如Bitmap。如果未關閉則容易造成內存泄漏。最好是在activity中的onDestroy()中將Bitmap給close()并設置成null。

  • 注冊對象未注銷

????????常見場景:例如:BroadcastReceiver、EventBus未注銷造成的內存泄漏。

????????解決方案:應該在activity的onDestroy()中及時注銷/反注冊

  • 類的靜態變量持有大數據對象

????????常見場景:靜態變量存儲數據

????????解決方案:盡量避免使用靜態變量存儲數據。如果是大數據對象,建議使用數據庫存儲。

????????關于數據庫,可以查看我的這篇文章:【安卓筆記】Room數據庫的基本使用-CSDN博客

  • 單例造成的內存泄漏

????????常見場景:使用了Activity的Context,但是被外部持有,導致無法回收。

????????解決方案:優先使用Application的Context,如需使用Activity的Context,可以在傳入Context時使用弱引用進行封裝,然后在使用到的地方從弱引用中獲取Context。如果獲取不到,則return即可。

  • 非靜態內部類的靜態實例

????????常見場景:實例化了 非靜態內部類的靜態實例

????????解決方案:該實例的生命周期和應用一樣長,這就會導致該靜態實例一直持有該Activity的引用,activity的內存資源不能正常回收。此時,我們可以將該內部類設置為靜態內部類或者將該內部類抽取出來封裝成一個單例,如果需要使用Context,盡量使用Application的Context。如果一定要使用Activity的Context,記得用完后要置空,讓GC可以回收。

  • Handler臨時性內存泄漏

????????常見場景:Handler是非靜態,并且創建在Activity或者Service中。

????????解決方案:Message發出之后存儲在MessageQueue中。在Message中存在一個target,它是Handler的一個引用。Message在Queue中存在的時間過長,就會導致Handler無法被回收。如果Handler是非靜態的,則會導致Activity或者Service不會被回收。并且消息隊列實在一個Looper線程中不斷地輪詢處理消息。當這個Acitivity退出時,消息隊列中還有未處理的消息或者正在處理的消息,并且消息隊列中的Message持有Handler實例的引用,Handler又持有Activity的引用,所以導致該Activity的內存資源無法及時回收,引發內存泄漏。

  • 容器中的對象沒清理造成的內存泄漏

????????解決方案:在退出程序之前,將集合.clear(),然后設置為null,再退出程序。

  • WebView

????????常見場景:WebView大多都存在內存泄漏的問題。

????????解決方案:在應用中只要使用一次WebView,內存就不會被釋放掉。我們可以為WebView開啟一個單獨的進程,使用AIDL與應用的主進程進行通信。WebView所在的進程可以根據業務的需要選擇合適的時機進行銷毀,達到正常釋放內存的目的。

  • 使用ListView時造成的內存泄漏

????????解決方案:在構造Adapter時,使用緩存的convertView


OOM比較難找到問題的根源,就是因為大部分OOM的問題,都是內存先被吃完了,最后由正常代碼引發OOM,所以,日志中基本上只能看到最后的觸發導致。這往往不是根本原因。

OOM的原因分類:

1. Java堆內存溢出(內存不夠)

2. 無足夠連續內存空間(內存夠,但大部分都是碎片化)

3. FD數量超出限制(文件句柄(FD: 文件句柄)泄漏)

4. 線程數量超出限制(線程數量泄漏)

5. 虛擬內存不足

2. 一些散裝知識

Java的對象生命周期

Java的對象生命周期(Java Object Life Cycle)(有大概了解就行了)

Created 創建

in use 應用

Invisible 不可見

Unreachable 不可達

Collected 收集

Finalized 終結

Deallocated 對象空間重新分配??


Java的四種引用

強引用:=

????????強引用:被GC掃描到了,也不會回收。容易造成OOM

軟引用:SoftReference

????????軟引用:內存不足時,會回收

弱引用:WeakRefoerence

????????弱引用:GC掃描到了,會被回收掉

虛引用:PhantomReference?

????????虛引用:相當于沒有被引用

3. Android內存分析命令

  • dumpsys meminfo
  • procrank
  • cat/proc/meminfo
  • free
  • showmap
  • vmstat
  • top -n 1

3.0 一些條件

1. 手機需要root(Android模擬器也行)

2. 需要使用adb

3. 進入 adb shell,再操作以下命令

3.1?dumpsys meminfo

功能:大概判斷哪個頁面有內存泄漏

內存指標概念

item全稱含義等價
USSUnique Set Size物理內存進程獨占的內存
PSSProportional Set Size物理內存PSS = USS + 按比例包含共享庫
RSSResident Set Size物理內存RSS = USS + 包含共享庫
VSSVirtual Set Size虛擬內存VSS = RSS + 未分配實際物理內存

總結:VSS >= RSS >= PSS >= USS,但 /dev/kgsl-3d0部分必須考慮VSS

一般只看PSS。

這邊說一下如何判斷activity中的view是否有內存泄漏:

例如從Aactivity進入Bactivity,再返回再進入,多次之后。

使用dumpsys meminfo命令,在命令提示符窗口最下方找到Objects組,查看ViewRootImpl數據,如果該數據在每次activity進入時都會增加。則代表該activity持有的view內存泄漏了

3.2 procrank

功能:獲取所有進程的內存使用情況,順序以PSS的大小從大到小排列。

procrank比dumpsys meminfo,更詳細輸出 VSS/RSS/PSS/USS內存指標。

例如:最后一行輸出以下6個指標:

totalfreebufferscachedshmemslab
2857032K998088K78060K78060K312K92392K

3.3 cat /proc/meminfo

功能:查看更加詳細的內存信息

3.4 free

功能:查看可用內存。單位KB。

該命令比較簡單、輕量,專注于查看剩余內存的情況。數據來源于3.3 cat /proc/meminfo

3.5 showmap

?功能:用于查看虛擬地址區域的內存情況

用法:

showmap -a [pid]

  • start addr和end addr:?分別代表進程空間的起止虛擬地址
  • virtual size/RSS/PSS:具體看3.1中的介紹
  • shared clean:代表多個進程的虛擬地址可指向這塊物理空間
  • shared:共享數據
  • private:該進程私有數據
  • clean:干凈數據,該內存數據與disk數據一致。當內存緊張時,可直接釋放內存,不需要回寫到disk
  • dirty:臟數據,需要回寫到disk,才能被釋放。?

3.6 vmstat

功能:不僅可以查看內存情況,還可以查看進程運行隊列、系統切換、CPU時間占比等。(可以周期性動態輸出)

用法:

vmstat [ -n iterations ] [ -d delay ] [ -r header_repeat ]

解釋:

-n iterations:數據循環輸出的次數

-d delay:兩次數據間的延遲時長(單位:s)

-r header_repeat:循環次數

3.7 top -n 1?

op命令是Linux下常用的性能分析工具,可以實時顯示系統中各個進程的資源占用情況。

功能:顯示當前系統正在執行的進程的相關信息:進程ID、內存占用率、CPU占用率等

用法:

top [參數]

參數:

-b 批處理

-c 顯示完整的治命令

-l 忽略失效過程

-s 保密模式

-S 累積模式

-i<時間> 設置時間間隔

-u<用戶名> 指定用戶名

-p<進程號> 指定進程

-n<次數> 循環顯示的次數?

3.8 總結

1. dumpsys meminfo 適用場景:查看進程的oom adj、dalvik/native等區域內存使用情況、某個進程/apk的內存情況

2. procrank 適用場景:查看進程的VSS/RSS/PSS/USS

3. cat /proc/meminfo 適用場景:查看系統的詳盡內存信息,包含內核情況

4. free 適用場景:只查看系統的可用內存

5. showmap 適用場景:查看進程的虛擬地址空間的內存分配情況

6. vmstat 適用場景:周期性打印進程運行隊列、系統切換、CPU時間占比等情況

4. 常見分析工具

1. Memory Analyzer Tools

? ? ? ? 開發過程中,本地分析

2. Memory Profiler(本文不介紹)

? ? ? ? Android Studio 自帶的分析工具

3. LeakCanary(本文不介紹)

? ? ? ? 線上版本集成該工具分析

5. MAT(Memory Analyzer Tools)工具的使用

工具下載地址:Downloads | The Eclipse Foundation

5.1 .hprof文件分析

????????5.1.1?儲存.hprof文件到手機內

public static void createDumpFile(Context context) {// 目錄路徑String LOG_PATH = "/dump.gc/";// 文件名稱SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd_HH.mm.ssss");String createTime = sdf.format(new Date(System.currentTimeMillis()));// 手機環境String externalStorageState = Environment.getExternalStorageState();// 如果有SD卡if (Environment.MEDIA_MOUNTED.equals(externalStorageState)) {// 文件路徑File file = new File(Environment.getExternalStorageDirectory().getPath() + LOG_PATH);if (!file.exists()) {// 沒有該路徑就創建file.mkdirs();}String hprofPath = file.getAbsolutePath();if (!hprofPath.endsWith("/")) {hprofPath += "/";}// 文件名稱hprofPath += createTime + ".hprof";try {// dump儲存.hprof文件android.os.Debug.dumpHprofData(hprofPath);} catch (IOException e) {throw new RuntimeException(e);}}
}

在執行完需要查看是否OOM的代碼后,再調用該函數,就可以在 手機路徑下看到.hprof文件:

sdcard/dump.gc/yyyy-MM-dd_HH.mm.ssss.hprof

????????5.1.2??MAT工具打開文件

此時無法用MAT工具直接打開文件,需要轉換。轉換的命令如下:

hprof-conv dump1.hprof converted-dump2.hprof

解釋

dump1.hprof:轉換前的文件

dump2.hprof:轉換后的文件

如果上面命令無效,請使用以下命令:

hprof-conv.exe dump1.hprof dump2.hprof

解釋:

dump1.hprof:轉換前的文件

dump2.hprof:轉換后的文件

(提一嘴,該命令的環境路徑在 android/sdk/platform-tools 下)?

轉換后就可以打開了。?

????????5.1.3 查看.hprof文件

其中 Problem Suspect 為工具猜想泄漏可能。不重要

首先點擊柱狀圖,按對象查看,出現下面的列表:(柱狀圖右側的按鈕,自行測試一下就好了。例如:按線程查看)

然后第二步選擇按package

?選擇完這個后,界面就會變成如下:

這樣,就可以比較直觀、比較友好的查看包名下的對象了。

????????5.1.4 具體對象查看?

我們在該頁面下,右鍵鼠標,選擇List objects后,右側有兩個選項,如下圖:

with outgoing references

查看該對象持有誰

with incoming references?

查看誰持有該對象?

?5.2 MAT中淺堆和深堆

Shallow Heap:淺堆

對象本身占用的內存

Retained Heap:深堆

統計結果:本身占用內存 + 引用的對象占用內存

例如下圖:

假設ABCDEFG各占用10個內存大小。但是A持有BC,B持有DE,C持有FG。所以A的淺堆為自身:10,深堆為70(A自身+BDE引用 +CFG引用)?

在工具中,就是這一部分數據:

?5.2.1 找到深堆異常

通過對深堆的查看,就可以知道 哪個對象占用內存過大。然后通過incoming和outging的引用關系查看,就可以查詢上下關系,找到內存泄漏的點。例如某個對象被多處引用,被持久化導致無法釋放。

本文來自互聯網用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務,不擁有所有權,不承擔相關法律責任。
如若轉載,請注明出處:http://www.pswp.cn/news/916722.shtml
繁體地址,請注明出處:http://hk.pswp.cn/news/916722.shtml
英文地址,請注明出處:http://en.pswp.cn/news/916722.shtml

如若內容造成侵權/違法違規/事實不符,請聯系多彩編程網進行投訴反饋email:809451989@qq.com,一經查實,立即刪除!

相關文章

持續集成CI與自動化測試

Python接口自動化測試零基礎入門到精通&#xff08;2025最新版&#xff09;

Spring 策略模式實現

Spring 策略模式實現&#xff1a;工廠方法與自動注入詳解 1. 背景介紹 在復雜的業務系統中,我們常常需要根據不同的場景選擇不同的處理策略。本文將詳細介紹在 Spring 框架中實現策略模式的兩種主要方法。 2. 方案一: 手動注冊工廠模式 2.1 定義工廠類 Component public class …

機器學習——線性回歸(LinearRegression)

Python 線性回歸詳解&#xff1a;從原理到實戰線性回歸&#xff08;Linear Regression&#xff09;是機器學習中最基礎也是最重要的算法之一&#xff0c;廣泛應用于預測分析領域&#xff0c;例如房價預測、銷售額預測等。本文將帶你從理論出發&#xff0c;用 Python 手把手實現…

H.264視頻的RTP有效載荷格式(翻譯自:RFC6184 第5節 RTP有效載荷格式)

RTP協議格式 RFC地址&#xff1a;https://datatracker.ietf.org/doc/html/rfc6184 RTP報頭的格式在RFC3550中指定 0 1 2 30 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1------------------------…

秒級構建消息驅動架構:描述事件流程,生成 Spring Cloud Stream+RabbitMQ 代碼

在消息驅動架構開發中&#xff0c;Spring Cloud Stream 與 RabbitMQ 的整合往往需要手動配置綁定器、定義消息通道、編寫消費邏輯&#xff0c;流程繁瑣且易出錯。而飛算JavaAI 作為高效的 IDE 插件&#xff0c;能讓開發者通過自然語言描述事件流程&#xff0c;自動生成可運行的…

從零搭建3D激光slam框架-基于mid360雷達節點實現

目錄 MID360雷達介紹 雷達SDK編譯與測試 雷達驅動的修改、編譯與測試 去ros的編譯方式 livox_ros_driver2的代碼框架介紹 livox_ros_driver2編譯 雷達IP配置文件介紹 常見問題介紹 優化改進 MID360雷達介紹 1 硬件介紹&#xff1a; livox-mid360是大疆的一款非重復掃描…

【Spring】日志級別的分類和使用

文章目錄介紹日志級別的分類日志級別的順序日志級別的使用介紹 日志級別代表著日志信息對應問題的嚴重性&#xff0c;為了更快的篩選符合目標的日志信息 試想一下這樣的場景&#xff0c;假設你是一家 2 萬人公司的老板&#xff0c;如果每個員工的日常工作和瑣碎的信息都要反饋…

【C++】第十九節—一文萬字詳解 | AVL樹實現

好久不見&#xff0c;我是云邊有個稻草人&#xff0c;偶爾中二博主與你分享C領域專業知識^(*&#xffe3;(oo)&#xffe3;)^ 《C》—本篇文章所屬專欄—持續更新中—歡迎訂閱~喔 目錄 一、AVL的概念 二、AVL樹的實現 2.1 AVL樹的結構 2.2 AVL樹的插入 【AVL樹插入?個值…

【Delphi】快速理解泛型(Generics)

Delphi的泛型&#xff08;generics&#xff09;是一項強大的特性&#xff0c;它使得代碼更加靈活、類型安全&#xff0c;并且可以實現各種通用的數據結構和算法。下面我將為你詳細介紹Delphi中的泛型&#xff0c;包括基本概念、語法、常用實例&#xff0c;以及使用建議。Delphi…

Java Stream流的使用

獲取Stream流 單列集合直接使用stream()方法 List<String> list Arrays.asList("a", "b", "c"); Stream<String> stream list.stream(); // 獲取順序流數組使用靜態方法Arrays.stream() String[] array {"a", "b&…

前端實現添加水印,兩種方式

一、自定義指令的方式/*需求&#xff1a;給整個頁面添加背景水印。思路&#xff1a;1、使用 canvas 特性生成 base64 格式的圖片文件&#xff0c;設置其字體大小&#xff0c;顏色等。2、將其設置為背景圖片&#xff0c;從而實現頁面或組件水印效果使用&#xff1a;設置水印文案…

使用LangChain構建法庭預定智能體:結合vLLM部署的Qwen3-32B模型

文章目錄 技術架構概述 核心實現步驟 1. 配置vLLM與Qwen3-32B模型 2. 定義工具(Tools) 3. 構建Agent系統 4. 運行與交互 關鍵技術亮點 1. 工具調用自動化 2. Hermes解析器優勢 3. 對話記憶管理 實際運行效果 性能優化建議 擴展應用場景 總結 在人工智能應用開發中,如何讓大語…

vscode開發微信小程序

下載插件 插件下載位置 1.微信小程序開發工具 2.vscode weapp api 3.vscode wxml 4.vscode-wechat 創建項目 終端運行命令 cd 到要創建項目的目錄執行命令&#xff1a;vue create -p dcloudio/uni-preset-vue test test就是項目名稱 選擇默認模板&#xff0c;回車 出現下圖這…

板凳-------Mysql cookbook學習 (十二--------3_3)

https://cloud.tencent.com/developer/article/1454690 侯哥的Python分享 # 創建節點 class Node(object):def __init__(self,item):self.element itemself.next None# 創建單鏈表類 class SingleLinkList(object):def __init__(self):self.header Noneself.length 0# 1、判…

Flutter開發實戰之CI/CD與發布流程

第12章:CI/CD與發布流程 在前面的章節中,我們學習了Flutter應用開發的各個方面,從基礎UI構建到復雜的狀態管理,從網絡請求到本地存儲。現在,我們將探討一個同樣重要但常被忽視的話題:如何將我們精心開發的應用高效、可靠地發布到各大應用商店。 想象一下,你花費了數月…

ElasticSearch 的3種數據遷移方案

在實際工作中&#xff0c;我們經常會遇到需要將自建的 Elasticsearch 遷移上云&#xff0c;或者遷移到其他 ES 集群的情況。這時&#xff0c;選擇合適的數據遷移方案就顯得尤為重要啦。今天就來給大家介紹三種常用的遷移方案&#xff0c;分別是 COS 快照、logstash 和 elastics…

MySQL 中的“雙路排序”與“單路排序”:原理、判別與實戰調優

一句話導讀 ORDER BY 不能走索引時&#xff0c;MySQL 會在 Server 層做一次 filesort。內部實現分 單路&#xff08;全字段&#xff09; 與 雙路&#xff08;rowid&#xff09; 兩種&#xff1b;了解它們的觸發條件、判別方法與調優思路&#xff0c;是 SQL 性能優化的必修課。一…

OpenLayers 綜合案例-信息窗體-彈窗

看過的知識不等于學會。唯有用心總結、系統記錄&#xff0c;并通過溫故知新反復實踐&#xff0c;才能真正掌握一二 作為一名摸爬滾打三年的前端開發&#xff0c;開源社區給了我飯碗&#xff0c;我也將所學的知識體系回饋給大家&#xff0c;助你少走彎路&#xff01; OpenLayers…

GaussDB 開發基本規范

1 集中式1.1數據庫價值特性推薦特性分類特性列表說明表類型PARTITION表數據分區存儲引擎行存儲按行順序存儲表&#xff0c;建議點查&#xff0c;增刪改操作較多場景下使用事務事務塊顯式啟動事務單語句事務不顯式啟動事務&#xff0c;單語句即為事務擴容在線擴容擴節點和數據重…

工作中使用git可能遇到的場景

1.main歷史發布版本出問題需要查看&#xff0c;怎么切換歷史發布版本&#xff1f;git reset --hard commitid 更新本地庫和代碼2.A分支的代碼已經做過一些功能&#xff0c;想遷移到B分支當前在A分支git checkout B &#xff08;切換到B分支&#xff09;git cherry-pick A的com…