雲計算

高德地圖駕車導航內存優化原理與實戰

​背景

根據Apple官方WWDC的回答,減少內存可以讓用戶體驗到更快的啟動速度,不會因為內存過大而導致Crash,可以讓APP存活的更久。

對於高德地圖來說,根據線上數據的分析,內存過高會導致導航過程中系統強殺OOM。尤其區別於其他APP的地方是,一般APP只需要關注前臺內存過高的系統強殺FOOM,高德地圖有不少用戶使用後臺導航,所以也需要關注後臺的內存過高導致的系統強殺BOOM,且後臺強殺較前臺強殺更為嚴重。為了提升用戶體驗,內存治理迫在眉睫。

原理剖析

OOM

OOM是Out of Memory的縮寫。在iOS APP中如果內存超了,系統會把APP直接殺死,一種另類的Crash,且無法捕獲。發現OOM時,我們可以從設備->隱私->分析與改進->分析數據中找到以JetsamEvent開頭的日誌,日誌裡面記錄了很多信息:手機設備信息、系統版本、內存大小、CPU時間等。

Jetsam

Jetsam是iOS系統的一種資源管理機制。不同於MacOS、Linux、Windows等,iOS中沒有內存交換空間,所以在設備整體內存緊張時,系統會將一些優先級不高或者佔用內存過大的直接Kill掉。

通過iOS開源的XNU內核源碼可以分析到:

  • 每個進程在內核中都存在一個優先級列表,JetSam在受到內存壓力時會從優先級列表最低的進程開始嘗試殺死,直到內存水位恢復到正常水位。
  • Jetsam是通過get_task_phys_footprint獲取到phys_footprint的值,來決定要不要殺掉應用。

Jetsam機制清理策略可以總結為以下幾點:

  • 單個APP物理內存佔用超過上限會被清理,不同的設備內存水位線不一樣。
  • 整個設備物理內存佔用受到壓力時,優先清理後臺應用,再清理前臺應用。
  • 優先清理內存佔用高的應用,再內存佔用低的應用。
  • 相比系統應用,會優先清理用戶應用。

Android端為Low Memory Killer:

  • 根據APP的優先級和使用總內存的多少,系統會在設備內存吃緊情況下強殺應用。
  • 內存吃緊的判斷取決於系統RSS(實際使用物理內存,包含共享庫佔用的全部內存)的大小。
  • 關鍵參數有3個:

1)oom_adj:在Framework層使用,代表進程的優先級,數值越高,優先級越低,越容易被殺死。

2)oom_adj threshold:在Framework層使用,代表oom_adj的內存閾值。Android Kernel會定時檢測當前剩餘內存是否低於這個閥值,若低於則殺死oom_adj ≥該閾值對應的oom_adj中,數值最大的進程,直到剩餘內存恢復至高於該閥值的狀態。

3)oom_score_adj:在Kernel層使用,由oom_adj換算而來,是殺死進程時實際使用的參數。

數據分析

phys_footprint獲取iOS應用總的物理內存,具體可以參考官方說明iOS Memory Deep Dive.

std::optional<size_t> memoryFootprint()
{
    task\_vm\_info\_data\_t vmInfo;
    mach\_msg\_type\_number\_t count = TASK\_VM\_INFO_COUNT;
    kern\_return\_t result = task\_info(mach\_task\_self(), TASK\_VM\_INFO, (task\_info_t) &vmInfo, &count);
    if (result != KERN_SUCCESS)
        return std::nullopt;
    return static\_cast<size\_t>(vmInfo.phys_footprint);
}

Instruments-VM Tracker可以用來分析具體內存分類,比如Malloc部分是堆內存,Webkit Malloc部分是JavaScriptCore佔用的內存等。需要注意的是每個分類的內存值 = Dirty Size + Swapped。

通過Instruments VM Tracker抓取導航中內存分佈進行對比分析。導航前臺靜置時,高德地圖的總內存數值非常高,其中IOKit、WebKit Malloc和Malloc堆內存為內存佔用大頭。

在分析過程中可以使用的工具很多,各有優缺點,需要配合使用,相互彌補。我們在分析的過程中主要用到Intruments VM Tracker、Allocations、Capture GPU Frame、MemGraph、dumpsys meminfo 、Graphics API Debugger、Arm Mobile Studio、AJX 內存分析工具、自研Malloc分析工具等。

  • IOKit內存為地圖渲染顯存部分。
  • WebKit Malloc內存為AJX JS業務內存。
  • Malloc堆內存,我們通過Hook Malloc分配內存的API,通過抓取堆棧分析具體內存消費者。

治理優化

根據上面的數據分析,很容易做出從大頭開始抓起的思路。我們在治理過程中的大體思路:

  • 分析數據:從內存大頭開始,分析各內存歸屬業務,以便業務進一步分析優化。
  • 內存治理:優化技術方案減少內存開銷、高低端機功能分級和智能容災(即內存告警時通過功能降級等策略釋放內存)。

分而治之

據數據分析,高德地圖三大內存消耗分別是地圖渲染(Graphic顯存)、功能業務(JavaScriptCore)和通用業務(Malloc)。我們也主要從這三個方面入手優化。

地圖Graphic顯存優化

Xcode自帶Debug工具Capture GPU Frame,可以分析出具體顯存佔用,顯存主要分為紋理Texture部分和Buffer部分,通過詳細的地址信息分析具體消耗。Android端類似分析顯存工具可以用Google的Graphics API Debugger。

根據分析,Texture部分我們通過FBO繪製方式調整、矢量路口大圖背景優化、圖標跨頁面釋放、文字紋理優化、低端機關閉全屏抗鋸齒等減少顯存消耗。Buffer部分通過開啟低顯存模式、關閉四叉樹預加載、切後臺釋放緩存資源等。

Webkit Malloc優化

高德地圖使用的是自研的動態化方案,依賴於iOS系統提供的框架JavaScriptCore,使用的業務內存消耗大多會被系統歸類到WebKit Malloc,從系統工具Instruments上的VM Tracker可以看出。此處有兩個思路,一個是業務自身優化內存消耗,第二個是動態化引擎和框架優化內存消耗。

業務自身優化,動態化方案的IDE提供內存分析工具可以清晰的輸出具體業務內存消耗在什麼地方,便於業務同學分析是否合理。

動態化引擎和框架優化,我們通過優化對系統庫JavaScriptCore的使用方式,即多個JSContextRef上下文共享同一份JSContextGroupRef的方式。多個頁面可以共享一份框架代碼,從而減少內存開銷。

Malloc堆內存優化

iOS端堆內存分配基本上使用的libmalloc庫,其中包含以下幾個內存操作接口:

// c分配方法
void    *malloc(size\_t \_\_size) \_\_result\_use\_check \_\_alloc_size(1);
void    *calloc(size\_t \_\_count, size\_t \_\_size) \_\_result\_use\_check \_\_alloc_size(1,2);
void     free(void *);
void    \*realloc(void \*\_\_ptr, size\_t \_\_size) \_\_result\_use\_check \_\_alloc\_size(2);
void    *valloc(size\_t) \_\_alloc_size(1);

// block分配方法
// Create a heap based copy of a Block or simply add a reference to an existing one.
// This must be paired with Block_release to recover memory, even when running
// under Objective-C Garbage Collection.
BLOCK\_EXPORT void \*\_Block_copy(const void \*aBlock)
    \_\_OSX\_AVAILABLE\_STARTING(\_\_MAC\_10\_6, \_\_IPHONE\_3_2);

通過hook內存操作API記錄下內存分配的堆棧、大小,即可分析內存使用情況。

同時源碼中還存在一個全局鉤子函數malloc_logger ,可輸出Malloc過程中的日誌,定義如下:

// We set malloc_logger to NULL to disable logging, if we encounter errors
// during file writing
typedef void(malloc\_logger\_t)(uint32_t type,
        uintptr_t arg1,
        uintptr_t arg2,
        uintptr_t arg3,
        uintptr_t result,
        uint32\_t num\_hot\_frames\_to_skip);
extern malloc\_logger\_t *malloc_logger;

iOS堆內存分析方案,可通過hook malloc系列API,也可以設置malloc_logger的函數實現,即可記錄下堆內存使用情況。

此方案有幾個難點問題,每秒鐘內存分配的量級大、內存有分配有釋放需要高效查詢和堆棧反解聚合。為此我們設計了一套完整的Malloc堆內存分析方案,來滿足快速定位堆內存歸屬,以便分發到各自業務Owner分析優化。

統一管理

隨著業務的增長給高德地圖這個超級APP帶來了極大資源壓力,因此我們沉澱了一套自適應資源管理框架,來滿足不同業務場景在有限資源下能夠做到功能和體驗極致均衡。主要的設計思路是通過監測用戶設備等級、系統狀態、當前業務場景以及用戶行為,利用調度算法進行實時推算,統一管理協調APP當前資源狀態分配,對用戶當前不可見的內存等資源進行回收。

自適應資源管理框架-內存部分

可以根據不同的設備等級、業務場景、用戶行為和系統狀態來管理資源。各業務都可以很容易的接入此框架,目前已經應用到多個業務場景,均有不錯的收益。

數據驗收

通過三個版本的連續治理,前後臺導航場景均有50%的收益,同時Abort率也有10%~20%的收益。整體收益算是比較樂觀,但是隨之而來的挑戰是我們該如何守住成果。

長線管控

所謂打江山容易守江山難,如果沒有長線管控的方案,隨著業務的版本迭代,不出三五個版本就會將先前的優化消耗。為此我們構建了一套APM性能監控平臺,在研發測試階段發現並解決問題,不把問題帶上線。

APM性能監控平臺

為了將APP的性能做到日常監控,我們建設了一套線下「APM性能監控平臺」,平臺能夠支持常規業務場景的性能監控,包括:內存、CPU、流量等,能夠及時的發現問題並進行報警。再配合性能跟進流程,為客戶端性能保障把好最後一關。

內存分析工具

Xcode memory gauge:在Xcode的Debug navigator中,可以粗略查看內存佔用的情況。

Instruments - Allocations:可以查看虛擬內存佔用、堆信息、對象信息、調用棧信息、VM Regions信息等。可以利用這個工具分析內存,並針對地進行優化。

Instruments - Leaks:用於檢測內存洩漏。

Instruments - VM Tracker:可以查看內存佔用信息,查看各類型內存的佔用情況,比如dirty memory的大小等等,可以輔助分析內存過大、內存洩漏等原因。

Instruments - Virtual Memory Trace:有內存分頁的具體信息,具體可以參考WWDC 2016 - Syetem Trace in Depth。

Memory Resource Exceptions:從Xcode 10開始,內存佔用過大時,調試器能捕獲到EXC_RESOURCE RESOURCE_TYPE_MEMORY異常,並斷點在觸發異常拋出的地方。

Xcode Memory Debugger:Xcode中可以直接查看所有對象間的相互依賴關係,可以非常方便的查找循環引用的問題。同時,還可以將這些信息導出為memgraph文件。

memgraph + 命令行指令:結合上一步輸出的memgraph文件,可以通過一些指令來分析內存情況。vmmap可以打印出進程信息,以及VMRegions的信息等,結合grep可以查看指定VMRegion的信息。leaks可追蹤堆中的對象,從而查看內存洩漏、堆棧信息等。heap會打印出堆中所有信息,方便追蹤內存佔用較大的對象。malloc_history可以查看heap指令得到的對象的堆棧信息,從而方便地發現問題。

總結:malloc_history ===> Creation;leaks ===> Reference;heap & vmmap ===> Size。

MetricKit:iOS 13新推出的監控框架,用於收集和處理電池和性能指標。當用戶使用APP的時候,iOS會記錄各項指標,然後發送到蘋果服務端上,並自動生成相關的可視化報告。通過Window -> Organizer -> Metrics可查,包括電池、啟動時間、卡頓情況、內存情況、磁盤讀寫五部分。也可以MetricKit集成到工程裡,將數據上傳到自己的服務進行分析。

MLeaksFinder:通過判斷UIViewController被銷燬後其子view是否也都被銷燬,可以在不入侵代碼的情況下檢測內存洩漏。

Graphics API Debugger:Google開源的一系列的Graphics調試工具,可以檢查、微調、重播應用對圖形驅動的API調用。

Arm Mobile Studio: 專業級GPU分析工具。

Leave a Reply

Your email address will not be published. Required fields are marked *