聊聊 MIUI 的「原子內(nèi)存」怎樣解決「殺后臺」的問題?
比起最近雷布斯的個人演講,其實我更好奇 MIUI 12.5 增強版(超級 Bug-list 修復(fù)版)推出的那四個性能優(yōu)化項目:
焦點計算 - 處理器智能調(diào)度機制
原子內(nèi)存 - 精細(xì)化內(nèi)存管理機制
液態(tài)存儲 - 文件存儲管理機制
智能均衡 - 對旗艦硬件性能的智能調(diào)配
作為技術(shù)愛好者,這個 「原子內(nèi)存」 還真是提起了我的興趣,因為其他三項還算比較好理解。畢竟內(nèi)存管理這件事,是操作系統(tǒng)領(lǐng)域幾乎永恒的課題。
▍官方解釋
我們先來了解下 MIUI 官方網(wǎng)站是如何介紹原子內(nèi)存的。官網(wǎng)的海報動效還是闡釋得比較清晰明了(辛苦設(shè)計師小姐姐,不知道又加了多少班)。
*開始是幾個獨立的應(yīng)用各自占有一定的內(nèi)存,看看這綠,這不就是微信綠。看看這藍(lán),支付寶?看看這橙,這啥,淘寶吧估計是,肯定不是小米自己。
然后,第二步是拆分應(yīng)用內(nèi)存,結(jié)束不重要的任務(wù),剛才還是整塊的內(nèi)存占用現(xiàn)在在邏輯上被劃分為了大小不等(即不同功能占用內(nèi)存大小不同)的幾塊。這一步其實就是核心了,如何來劃分呢?按什么維度劃分呢?有沒有可能劃分失誤反而影響用戶體驗?zāi)兀课覀兒竺鎭砹摹?/p>
第三步,根據(jù)場景,進(jìn)一步精細(xì)壓縮。這一步也很關(guān)鍵,不僅能結(jié)束優(yōu)先級較低的任務(wù),還能對剩余的任務(wù)進(jìn)行內(nèi)存占用的壓縮。聽起來有點玄乎,其實問題還是和第二步類似,如何對內(nèi)存進(jìn)行壓縮?系統(tǒng)如何知道要壓縮哪些內(nèi)容呢?
帶著上面這些問題,我們來簡單聊一聊 MIUI 可能會如何實現(xiàn)這個原子內(nèi)存,也即是說,以下內(nèi)容均是基于個人經(jīng)驗提出的一些猜想,描述盡可能不那么硬核。
▍原理猜想
我們知道,現(xiàn)代操作系統(tǒng)基本都有進(jìn)程(process)和線程(thread)的概念,用書本上的話說就是:
進(jìn)程是資源分配的*小單位,線程是 CPU 調(diào)度的*小單位。
那么二者的關(guān)系,簡單概括便是:一個運行中的應(yīng)用程序就可以包含多個進(jìn)程,一個進(jìn)程又可以包含多個線程。
拿微信舉例來說,主功能聊天是一個進(jìn)程,內(nèi)置瀏覽器又是另一個進(jìn)程,進(jìn)程之間可以進(jìn)行通信,但又可以互不影響,比如瀏覽器崩潰了,不影響你聊天,這也是多進(jìn)程的好處之一。然后,在聊天進(jìn)程內(nèi)部,你下載表情包時會開啟另一個線程來完成任務(wù),而不會影響你的 UI 渲染線程,保證了交互的流暢性,這就是多線程的好處。
大致了解這些基本概念后,我們就可以繼續(xù)思考 MIUI 的原子內(nèi)存了。
Android 系統(tǒng)基于 Linux,當(dāng)然也有進(jìn)程的概念。我們平時俗稱的 「殺進(jìn)程」,一般就是指在最近任務(wù)中劃掉相應(yīng)的應(yīng)用來終止其運行,在你劃掉的那一瞬間,系統(tǒng)會殺掉此應(yīng)用相關(guān)聯(lián)的所有進(jìn)程。而我們俗稱的 「被殺后臺」,就是應(yīng)用在置于后臺且不可見的時候,被系統(tǒng)為了節(jié)約內(nèi)存等資源而殺掉,這對用戶而言是一種被動的殺進(jìn)程,某種程度上比較影響使用體驗。
在 Android 的官方文檔中關(guān)于內(nèi)存分配有這么一段話:
Android 平臺在運行時不會浪費可用的內(nèi)存。它會一直嘗試?yán)盟锌捎脙?nèi)存。例如,系統(tǒng)會在應(yīng)用關(guān)閉后將其保留在內(nèi)存中,以便用戶快速切回到這些應(yīng)用。因此,通常情況下,Android 設(shè)備在運行時幾乎沒有可用的內(nèi)存。要在重要系統(tǒng)進(jìn)程和許多用戶應(yīng)用之間正確分配內(nèi)存,內(nèi)存管理至關(guān)重要。
其實這個解釋和 Apple 官方之前建議用戶不要頻繁地手動殺進(jìn)程是一個道理。因為應(yīng)用啟動,進(jìn)程重新創(chuàng)建的開銷是很大的。此外,接觸過 Android 開發(fā)的同學(xué)都知道,系統(tǒng)在內(nèi)存不足的時候也會盡可能地通知各應(yīng)用,給了你 「體面」 處理自己的機會。
然而,很多時候國產(chǎn)手機廠商也是不得已而為之,因 Android 應(yīng)用可以在后臺運行的特性,不管是各類巨無霸應(yīng)用,還是小而美應(yīng)用,都在努力讓自己在后臺?;疃蛔袷亻_發(fā)規(guī)范,生怕用戶離它們而去。但這樣爭奇斗艷的后果就是用戶設(shè)備續(xù)航能力急劇下降,挨罵的往往就是手機系統(tǒng)廠商了。
「原子內(nèi)存」 想解決的便是這個問題,既能讓更多的應(yīng)用不被系統(tǒng)完全殺掉,也能減少系統(tǒng)資源的占用,在功耗和功能之間取得一個相對平衡。
拆分內(nèi)存
其實在 Android 應(yīng)用層面的開發(fā)當(dāng)中,中小型應(yīng)用的開發(fā)者日常很少會接觸到進(jìn)程的概念,更多的是 Android 自己抽象的一套開發(fā)框架。在其設(shè)計架構(gòu)中, Google 給開發(fā)者和用戶提供了 「四大組件」:
Activity - 可簡單理解成我們打開的每一個活動頁面,用于處理用戶交互的邏輯
Service - 服務(wù),和 Activity 的邏輯結(jié)構(gòu)類似,只不過沒有界面,比如用于播放音樂、下載文件等
BroadcastReceiver - 可理解為,Android 系統(tǒng)就是一個巨大的世界頻道,每個應(yīng)用都可以用小喇叭廣播消息,當(dāng)然也需要接收消息
ContentProvider - 數(shù)據(jù)存儲和提供的組件,內(nèi)含標(biāo)準(zhǔn)接口,不同應(yīng)用之間可通過此來共享內(nèi)容
這些組件是 Android 開發(fā)的基礎(chǔ),對于任意一個組件,它都可以獨占一個進(jìn)程(比如我們上面提到的內(nèi)置瀏覽器 WebView),也可以和其他組件共享同一個進(jìn)程,大多數(shù)情況都是后者。組件的本質(zhì)也就是系統(tǒng)創(chuàng)建的一個內(nèi)存對象,同時,每個組件都有自己的生命周期,在應(yīng)用進(jìn)程不被殺掉之前,可以提前結(jié)束(比如你打開登錄頁面完成登錄,這個 Activity 的使命就完成了,可以被系統(tǒng)回收資源)。
所以在我*時間看到 MIUI 原子內(nèi)存的宣傳時,就想到了對四大組件的精細(xì)化查殺。拆分內(nèi)存這一行為可能實質(zhì)上就是把應(yīng)用內(nèi)的各個組件進(jìn)行分類并按優(yōu)先級排序,銷毀低優(yōu)先級的組件,保證用戶可見和正在使用的組件活著就行了,如此就能節(jié)省很多內(nèi)存。當(dāng)用戶需要再次使用某組件<愛尬聊_尬聊百科>時,重新創(chuàng)建啟動便是。
又拿微信來舉例,當(dāng)你掃碼支付后,又回到聊天界面,聊了幾句再把微信置于后臺,這時候你去干別的事情了,系統(tǒng)是不是可以把剛才支付相關(guān)的資源都回收掉呢?
當(dāng)然,如果銷毀和創(chuàng)建的行為過于頻繁,也會消耗不少的系統(tǒng)資源,所以我猜測除了設(shè)計組件優(yōu)先級等基本策略,還會有一些機器學(xué)習(xí)的玩法在里面, 更精準(zhǔn)地識別哪些可以被銷毀 。在用戶使用過程中體驗會越來越好,這是一個雙向正反饋的過程。
最近在 MIUI 的官方論壇也看到一些解釋提到「進(jìn)程級」查殺,其實這個就更好理解了,因為本身很多業(yè)務(wù)相關(guān)性強的組件就在同一個進(jìn)程內(nèi),一個大型應(yīng)用往往有很多進(jìn)程在同時運行,那么系統(tǒng)也可以殺掉某些暫時用不到的進(jìn)程(相當(dāng)于還能一次性銷毀多個組件),而非粗暴地殺掉整個應(yīng)用導(dǎo)致用戶回來時一切都要重來。
精細(xì)壓縮
說到壓縮,我們可以從*基本的一些解釋中了解原理:
它就是移除多余的空白字符,插入單個的重復(fù)字符指出一個字符串中重復(fù)的字符,以及將小型的位串用頻繁使用的字符替代。
壓縮其實是一個很樸素的概念,當(dāng)你知道下一秒的畫面跟這一秒一致時,是不是可以少存儲幾幀數(shù)據(jù)。在內(nèi)存上,也是類似的方法,它是真正能夠節(jié)約物理空間的。
在 MIUI 的海報中,有個非常關(guān)鍵的前提就是:根據(jù)場景。也就是說,并不是無腦地對所有內(nèi)存占用進(jìn)行壓縮,而是根據(jù)用戶使用的場景,來判斷哪些進(jìn)程或者組件所占用的內(nèi)存可以壓縮,哪些*好不要壓縮,一切以用戶體驗為前提。這個壓縮的策略,和上述的拆分內(nèi)存類似,可能也會有機器學(xué)習(xí)的施展空間,當(dāng)然,用黑白名單來進(jìn)行簡單粗暴的實現(xiàn)也不是不可以。
精細(xì)壓縮這一步,從 Android 系統(tǒng)的體系結(jié)構(gòu)來講,總體可以分為兩方面,一是交換壓縮,二是垃圾回收。
交換壓縮
先看看官方介紹:
Android 設(shè)備包含三種不同類型的內(nèi)存:RAM、zRAM 和存儲器。
RAM 是*快的內(nèi)存類型,但其大小通常有限。高端設(shè)備通常具有*大的 RAM 容量。
zRAM 是用于交換空間的 RAM 分區(qū)。所有數(shù)據(jù)在放入 zRAM 時都會進(jìn)行壓縮,然后在從 zRAM 向外復(fù)制時進(jìn)行解壓縮。這部分 RAM 會隨著頁面進(jìn)出 zRAM 而增大或縮小。設(shè)備制造商可以設(shè)置 zRAM 大小上限。
存儲器中包含所有持久性數(shù)據(jù)(例如文件系統(tǒng)等),以及為所有應(yīng)用、庫和平臺添加的對象代碼。存儲器比另外兩種內(nèi)存的容量大得多。在 Android 上,存儲器不像在其他 Linux 實現(xiàn)上那樣用于交換空間(swap),因為頻繁寫入會導(dǎo)致這種內(nèi)存出現(xiàn)損壞,并縮短存儲媒介的使用壽命。
我們可以看出,zRAM 是內(nèi)存壓縮的關(guān)鍵所在,相當(dāng)于在內(nèi)存上劃分出一小塊區(qū)域用于非活躍內(nèi)存占用的壓縮存儲。這個 「z」,大概就是 zip 的意思吧。所以,在內(nèi)存壓縮的底層實現(xiàn)上,原生 Android 已經(jīng)搞定了,MIUI 不需要自行實現(xiàn)這些內(nèi)存管理機制,*多是在此基礎(chǔ)上根據(jù)用戶使用場景進(jìn)行算法優(yōu)化或者動態(tài)調(diào)整配置。
這里特別要注意的是,官方文檔提到:在 Android 上,存儲器不像在其他 Linux 實現(xiàn)上那樣用于交換空間。這一點也否認(rèn)了最近網(wǎng)上關(guān)于 MIUI 原子內(nèi)存的討論中 「就是 Linux 的 swap 機制而已」 之類的看法。
我個人認(rèn)為,Linux 的 swap 機制不會特別適用于移動設(shè)備,因為移動設(shè)備的存儲器基本上都是類似 SSD 的閃存,用作交換空間來存取內(nèi)存數(shù)據(jù)是非常減壽的,對手機來說,閃存壽命下降就意味著卡頓。即便是現(xiàn)在的電腦,也不會設(shè)置那么大的 swap 空間了,因為很多電腦內(nèi)存本身就很大。
垃圾回收
我們每一個應(yīng)用都運行在一個獨立的 Android 虛擬機之上,所以應(yīng)用的每一個進(jìn)程也可叫做虛擬機進(jìn)程。Android 虛擬機可以粗淺地看作 Java 虛擬機(JVM)的魔改版本,它不能運行原生 Java 程序。垃圾回收(GC)是 JVM 內(nèi)存管理的核心機制之一。這里就不贅述了,簡單來說就是系統(tǒng)對內(nèi)存單元的整理和清除。
垃圾回收和剛才的交換壓縮一樣,它們管理內(nèi)存的粒度都是 「內(nèi)存頁(page)」,而不是進(jìn)程和 Android 組件了,對于上層的應(yīng)用來說,是沒有什么感知的,系統(tǒng)底層在默默地自我調(diào)節(jié)。
MIUI 原子內(nèi)存在垃圾回收層面應(yīng)該不會去做什么改動,因為 GC 機制比較底層,而且也相對成熟穩(wěn)定,MIUI 做的事情更多是用戶層面的,業(yè)務(wù)層面的,屬于 GC 的上層調(diào)用者。
精細(xì)壓縮可能會在何時觸發(fā)、如何更有效地觸發(fā)垃圾回收上面下功夫。
▍后話
做了這么多猜測,其實也沒有特別深入的技術(shù)細(xì)節(jié)討論,但我在查閱研究過程中,還是能感受到 MIUI 在系統(tǒng)層面的努力,而不僅僅限于做 「UI」。畢竟這么花里胡哨的功能命名都唱出來了,怎么也得有點干貨才行吧。
此外,寫完了我才想到忘了提及以前的 Android 玩機工具寫輪眼,它的主要功能就是由用戶自行決定銷毀哪些組件,MIUI 原子內(nèi)存或許也從中借鑒了一些思路。
還有一點收獲就是,現(xiàn)在我們可以在很多移動設(shè)備用戶場景遇到 AI 技術(shù)的落地或者是落地的可能性,借此機會,有空的話可以再跟大家聊聊端側(cè) AI。
13 號當(dāng)晚我也順利升級到了這個 MIUI 12.5 增強版,感受了一下,同時開 10 個常用 App,后臺被殺的情況確實有所改善,而且剩余內(nèi)存也比以前多了。對普通用戶來說這就是簡單真實的感受,有效果,說明有點東西。