前言
由于Android系統中每個APP所占用的記憶體分為兩部分:java記憶體和native記憶體。
java申請的記憶體是在vm的堆記憶體中,如果超過vm設定的記憶體限制,則會爆出out of memery的異常。native申請的記憶體則沒有限制,native層受native程序對記憶體大小的限制。
對于java層記憶體的跟蹤、洩漏檢測目前有leakcanary的第三方庫比較好用,可以在開發階段對記憶體洩漏做很好的檢測跟蹤,接入、使用都比較簡單。
如果想要bigger更高點,可以使用Android studio提供的Memory Profiler工具。在3.2版本中改為了Profile app,點選圖示之後,會編譯安裝app,app啟動之後會在底部的工具欄出現一個profiler的圖示,即可看到下圖:

其中的memory一欄就是我們今天說的内容。
關于memory界面的介紹可以檢視官方文檔:
使用 Memory Profiler 檢視 Java 堆和記憶體配置設定
檢視記憶體配置設定:
如果閱讀了官方文檔,就會知道可以在某個時間點的記憶體配置設定,具體操作就是在手機上面操作,然後在可疑點直接單機,就可以檢視該時間點的記憶體配置設定情況。
如圖所示,點選之後,就可以看到如下圖所示:
下半部分就是這個時間點目前記憶體中的對象資料,可以按類查找,也可以按照報名查找。
如果懷疑某個對象洩漏了,那麼在手機上做對應的操作(包括建立對象、釋放對象)。
比如懷疑某個fragment對象洩漏了,那我先打開fragment,在關閉fragment.點選gc圖示,經過gc之後,檢視記憶體中是否有對象存在。這裡以helpFragment為例。經過添加helpFragment,在移除fragment之後,選擇一個時間點,檢視記憶體對象配置設定情況。
找到HelpFragment,單擊,在右側的Instance View的tab中,發現記憶體中存在3個執行個體。
右下的call stack顯示的是該對象的建立路徑,即對象建立過程的調用棧。
這裡說明這裡存在了記憶體洩漏。那麼問題來了,我想知道具體是哪些對象引用這些對象,我想檢視引用鍊來解決記憶體洩漏該怎麼辦呢。這就不得不說Dump java heap功能。
Dump java heap
官方文檔翻譯為堆轉儲,它不僅可以檢視記憶體的對象配置設定,還能看到對象的引用鍊,進而确認最終的記憶體洩漏。
操作方法:在gc之後,選擇某一段時間,點選Dump Java heap
進過幾秒之後,再次點選,就會生成如下圖:(下圖中包含兩次dump java heap:灰色透明層,藍色透明層,這裡看的是右邊藍色透明層)
在右下方的References一欄即可看到該對象被其他對象引用的情況。
注意:這一欄也包括很多該類對象中的内部對象引用該對象的方法,比如将該對象作為參數,傳到内部成員對象中,則該内部成員對象也會出現在這一欄。比如HelpCreateVIiewManager這一欄。該manager是在fragment在onViewCreate階段建立的對象,用來作為該fragment的controller。是以在分析洩漏時,這類可以排除掉。
經過查找,發現是YxSlideHelper類中的對象引用導緻了記憶體洩漏
右鍵單擊,可以看到兩個菜單:Jump to Source 和Go to instance。
JumpTo Source是跳轉到具體的引用的對象建立的地方,也就是YxSlideHelper類的vald對象那一行。Go to instance是相當于檢視YxSlideHelper的引用鍊。
點選Jump To Source,跳轉到YxSlideHelper類的源碼,發現代碼如下:
這裡注冊了一個FragmentLifecycleCallbacks對象到activity的FragmentManager中。到這裡還是沒問題,問題是是onFragmentAttached()方法中使用到了添加進來的HelpFragment。根據靜态内部類的知識,相當于該FragmentLifecycleCallbacks對象中持有了helpFragment對象,而FragmentLifecycleCallbacks對象被activity中的FragmentManager持有,activity沒有銷毀的話,自然會導緻洩漏。解決方法很簡單,在HelpFragment被銷毀的時候,調用一次fragmentmanger的unregisterFragmentLifecycleCallbacks()方法即可。
fragmentManager中的registerFragmentLifecycleCallbacks和unregisterFragmentLifecycleCallbacks源碼如下:
引用鍊:
Activity->fragmentmanager->mLifecycleCallbacks->FragmentLifecycleCallbacks ->fragment.
總結:
這裡介紹的是如何使用Android studio提供的profiler App工具,結合一次實際使用,來定位記憶體洩漏問題。如果有具體的懷疑對象,則會比較友善的定位,如果沒有懷疑對象,隻能通過多次打開關閉或者借助monkey runner來模拟,檢視記憶體增長情況,gc之後,結合代碼,檢視記憶體中遺留的本該被銷毀的對象。
注意:一定要多次gc,有些對象引用太多,一次gc之後可能還是會存在。