Perfdog 玩轉記憶體洩漏
背景交代
最近 QC 同學在跑遊戲的過程中發現玩的時間久了遊戲會發生閃退,經過蒐集資訊後排除了功能性 bug 的
一.判斷是否是記憶體洩露
拿到真機,USB 連線,殺掉多餘後臺程序,開啟 Perfdog,接下來一頓操作猛如虎,Perfdog 具體操作不在贅述,有關 perfdog 怎麼使用的教程可以參考
Perfdog 使用教程
拿到記憶體趨勢圖
使用手機
此圖一出,基本就可以斷定記憶體洩露了,這是正常玩遊戲,遊戲執行了 30 分鐘的記憶體趨勢圖;
結論:,記憶體持續上升,存在記憶體洩露。
一個優秀的遊戲通常情況記憶體是有上升有回落,多次執行同一個功能也不會導致記憶體功能持續上升;
呈現出起伏狀態,比如:
知道了存在記憶體洩露,下面就要開始分析有可能是哪裡導致的記憶體洩露;
二.分析洩露原因
一般針對 unity 遊戲來說,記憶體瓶頸是資源和 Mono 堆記憶體,兩部分;
以下是 unity 遊戲程式在執行時的記憶體分配概況
先簡單介紹下 Mono,unity 使用 Mono 機制來完成跨平臺的操作,就好像 JAVA 使用虛擬機器來完成跨平臺操作一樣,Mono 也是一種跨平臺的實現。跨平臺其實現原理在於使用了叫 CIL(Common Intermediate Language 通用中間語言,也叫做 MSIL 微軟中間語言)的一種程式碼指令集,CIL 可以在任何支援 CLI(通用語言基礎結構)的環境中執行,就像.NET 是微軟對這一標準的實現,Mono 則是對 CLI 的又一實現。由於 CIL 能執行在所有支援 CLI 的環境中,例如剛剛提到的.NET 執行時以及 Mono 執行時,也就是說和具體的平臺或者 CPU 無關。
一般對於 unity 開發的遊戲來說,記憶體的開銷都是圍繞下面的三個方面:
1.資源記憶體的佔用;
2.引擎模組自身記憶體佔用;
3.託管堆記憶體佔用。
Mono 透過垃圾回收機制(GarbageCollect,簡稱 GC)對記憶體進行管理,可以自動地改變堆的大小來適應你所需要的記憶體,並且是可以適時地呼叫垃圾回收(GarbageCollection)操作來釋放已經不需要的記憶體。也就是說 Mono 會自動釋放一些記憶體,但要注意的是 GC 釋放的記憶體只會留給 mono 使用,並不會交還給作業系統,因此 mono 堆記憶體是隻增不減的。
這裡簡單介紹下 Mono 回收原理:
Mono 會跟蹤每次記憶體分配的動作,並維護一個分配物件表,當 GC 的時候,以全域性資料區和當前暫存器中的物件為根節點,按照引用關係進行遍歷,對於遍歷到的每一個物件,將其標記為活的(alive)。所有物件的被標記意味著該物件可以透過全域性物件或者當前上下文訪問到,而沒有被標記的物件則意味著該物件無法透過任何途徑訪問到,即該物件 “失聯” 了,GC 最終會將所有 “失聯” 的物件記憶體進行回收。
記憶體洩露定義
我們把物件已經不再需要使用卻沒有被 GC 回收的情況稱為 mono 記憶體洩漏。Mono 記憶體洩漏會使空閒記憶體減少,GC 頻繁,mono 堆不斷擴充,最終導致遊戲記憶體佔用的升高。最終導致記憶體過高,程序被作業系統 Kill 或者崩潰。簡單來說,也就是一些物件被例項化出來後沒有被釋放掉,一種儲存在記憶體中,新的物件又需要申請新的記憶體空間,導致記憶體不斷上升。
重點關注點
配置檔案的使用、紋理、網格、RenderTexture 和粒子系統;
比如頻繁的建立銷燬物件是否使用物件池,或者粒子,紋理等資源顯示過後是否被及時從記憶體中釋放,等等;
三.測試手段
1.首先通跑測試,確定問題確定原因,比如我上面透過通跑遊戲確定存在記憶體洩露;
2.縮小範圍,由於一個遊戲在執行的過程中場景比較複雜,上面的同跑並不能準確定位問題,所以我們要劃分場景測試,例如我在上面的通跑遊戲過程中包括以下場景,開啟關閉 UI 介面,戰鬥場景,切換地圖,升級武器等,如果沒有比較明顯的資料,那就要分別針對以下場景進行測試。比如 UI 場景可以反覆開啟關閉 UI 介面,戰鬥場景可以持續戰鬥掛機,反覆切換地圖等等,總之是把遊戲內進行的行為減少,細化要檢測的場景;
3.定位問題
如果某個場景發生記憶體洩露,邊定位到那個場景執行遊戲,而在遊戲執行時,相應的引擎也有一些工具可以檢視具體的程式碼使用情況,比如 unity 的 Profiler。
如果多個場景都出現記憶體洩露,那就要查詢這些場景所交叉的部分,比如通訊框架等;而本次經過多個場景的測試發現都存在洩露,最後經過排查發現是使用的通訊框架存在洩露問題。
四,Perfdog 記憶體相關簡介
通常情況下安卓可以輕鬆獲取到的記憶體有 4 種資料,我們也可以透過 ADB 來獲取,
VSS - Virtual Set Size 虛擬耗用記憶體(包含共享庫佔用的記憶體)
RSS - Resident Set Size 實際使用實體記憶體(包含共享庫佔用的記憶體)
PSS - Proportional Set Size 實際使用的實體記憶體(比例分配共享庫佔用的記憶體)
USS - Unique Set Size 程序獨自佔用的實體記憶體(不包含共享庫佔用的記憶體)
一般來說記憶體佔用大小有如下規律:VSS >= RSS >= PSS >= USS
而 Perfog 的 Memory 也就是 Android PSS Memory,也是我們通常來用作代表記憶體的資料,是實際使用的實體記憶體大小。
Swap Memory (Swap Memory,部分裝置支援 Swap 功能,在啟用 Swap 功能後,系統會對 PSS 記憶體進行壓縮,Swap 增加,PSS 會相應減少,由於壓縮會佔用 CPU 資源,同時相應會導致 FPS 降低)
Virtual Memory(VSS) 虛擬記憶體是計算機系統記憶體管理的一種技術。它使得應用程式認為它擁有連續的可用的記憶體(一個連續完整的地址空間),而實際上它通常是被分隔成多個實體記憶體碎片,還有部分暫時儲存在外部磁碟儲存器上,在需要時進行資料交換。
五、Perfdog 新功能初探
PerfDog 3.5 版本剛剛推出,新增一個最新的數值,CPU Usage(Normalized):規範化 CPU 利用率
官方給出的解釋為:
傳統計算方法:當前時刻 CPU 頻率下,CPU Usage = CPU 執行時間/CPU 總時間。
由於移動裝置 CPU 頻率時刻變化,用傳統 CPU 利用率計算方法,假定在低頻率時刻計算出 CPU 利用率=30%,和在 CPU 高頻時刻計算出 CPU 利用率=30%。同樣都是 30% 但效能消耗是完全不樣的,明顯高頻消耗更高。傳統 CPU 利用率已無法真實反映效能消耗。
所以我們需要一種規範化 (可量化) 的統計方式。將頻率因素考慮進去。
CPU Usage(Normalized)= (CPU 執行時間/CPU 總時間) * (當前時刻所有 CPU 頻率之和/所有 CPU 頻率最大值之和)。
PerfDog 兩種統計方式都有。CPU Usage 預設為規範化 CPU 利用率。建議使用規範化 CPU 利用率作為衡量效能指標。
具體的描述可以看這裡:規範化 CPU 利用率
嚐鮮體驗以下。測試使用過程和之前的一樣。來看看新增的資料對比
title:
CPU Usage 趨勢圖對比:
CPU Core Usage 趨勢圖對比:
從趨勢圖來看的話,實際上兩種演算法並無太大差異,但是精確到具體幀的使用率,差異會比較明顯,單純從效能的角度來說,傳統 CPU 利用率僅能從數值的角度體現手機的 CPU 使用程度,但是無法從效能使用程度的角度表達手機的 CPU 使用效率,就像前文所說,低頻率時刻計算出 CPU 利用率=30%,和在 CPU 高頻時刻計算出 CPU 利用率=30%。同樣都是 30% 但效能消耗是完全不樣的。規範化 CPU 利用率數值可以彌補這一缺點。目前的測試行業良莠不齊,規範指標較少,如果真的可以做到統一行業標準不失為一件好事。
相關文章
- 分析記憶體洩漏和goroutine洩漏記憶體Go
- jvm 記憶體洩漏JVM記憶體
- Android 記憶體洩漏Android記憶體
- Java記憶體洩漏Java記憶體
- js記憶體洩漏JS記憶體
- Android記憶體洩漏Android記憶體
- valgrind 記憶體洩漏分析記憶體
- 記憶體洩漏的原因記憶體
- 【記憶體洩漏和記憶體溢位】JavaScript之深入淺出理解記憶體洩漏和記憶體溢位記憶體溢位JavaScript
- JVM——記憶體洩漏與記憶體溢位JVM記憶體溢位
- 記憶體洩漏除錯工具記憶體除錯
- ThreadLocal真會記憶體洩漏?thread記憶體
- WebView引起的記憶體洩漏WebView記憶體
- .Net程式記憶體洩漏解析記憶體
- iOS檢測記憶體洩漏iOS記憶體
- Android記憶體洩漏場景Android記憶體
- ThreadLocal記憶體洩漏問題thread記憶體
- PHP 記憶體洩漏分析定位PHP記憶體
- JavaScript之記憶體洩漏【四】JavaScript記憶體
- 記憶體的分配與釋放,記憶體洩漏記憶體
- 1.記憶體優化(一)記憶體洩漏記憶體優化
- JavaScript之記憶體溢位和記憶體洩漏JavaScript記憶體溢位
- 納尼,Java 存在記憶體洩洩洩洩洩洩漏嗎?Java記憶體
- [Java基礎]記憶體洩漏和記憶體溢位Java記憶體溢位
- Java棧溢位|記憶體洩漏|記憶體溢位Java記憶體溢位
- 解決記憶體洩漏(1)-ApacheKylin InternalThreadLocalMap洩漏問題分析記憶體Apachethread
- ThreadLocal記憶體洩漏怎麼回事thread記憶體
- redisson記憶體洩漏問題排查Redis記憶體
- 深入瞭解 JavaScript 記憶體洩漏JavaScript記憶體
- 記憶體洩漏-原因、避免和定位記憶體
- linux程式之記憶體洩漏分析Linux記憶體
- Android中常見的記憶體洩漏Android記憶體
- .NET 記憶體洩漏的爭議記憶體
- Andriod專案記憶體洩漏流程記憶體
- Java記憶體洩漏解決之道Java記憶體
- Android備忘錄《記憶體洩漏》Android記憶體
- 小心遞迴中記憶體洩漏遞迴記憶體
- Swift的ARC和記憶體洩漏Swift記憶體