首頁 行業(yè) 活動 項目 快訊 文娛 時尚 娛樂 科技 汽車 綜合 生活

減少內(nèi)存占用最直接的表現(xiàn)是?

2021-03-19 13:21:46 來源:云加社區(qū)

一、內(nèi)存優(yōu)化的必要性

事實上,因為目前 iPhone 配備的內(nèi)存越來越高,當(dāng)內(nèi)存占用過高時,并不一定會超過系統(tǒng)設(shè)定的閾值而引發(fā)強(qiáng)殺進(jìn)程。

但這并不意味著減少內(nèi)存占用是沒有意義的,因為當(dāng)內(nèi)存占用過高時,很容易引起一系列的副作用。最直接的表現(xiàn)是 App Crash,當(dāng)然還有很多更為深遠(yuǎn)的副作用。

1. FOOM

FOOM 是最直接的影響了,當(dāng)內(nèi)存占用過多導(dǎo)致整個系統(tǒng)的可用內(nèi)存不足時,App所在的進(jìn)程容易被殺掉。而且相比于一般的 Crash 來說,F(xiàn)OOM 更難以檢測,并且也更難排查。

2. 限制并發(fā)數(shù)量

如果一個任務(wù)占用了過多的內(nèi)存,但總的內(nèi)存是有限的,那么任務(wù)的并發(fā)數(shù)將會受到直接限制。表現(xiàn)上就是 App 里某個功能可同時執(zhí)行的數(shù)量有限,或者可以同時顯示的內(nèi)容有數(shù)量限制。

同時,因為內(nèi)存是有限資源,當(dāng)占用內(nèi)存過多時,會容易導(dǎo)致操作系統(tǒng)殺掉其它 App 的進(jìn)程來給當(dāng)前的 App 提供足夠的內(nèi)存空間,這對用戶體驗是不利的。

3. 增加耗電

由于 iOS 系統(tǒng)的 Memory Compressor 的存在,當(dāng)可用內(nèi)存不足時,一部分 Dirty Page 會被壓縮存儲到磁盤中,當(dāng)用到這部分內(nèi)存時,再從磁盤里加載回來。這會造成 CPU 花費更多的時間來等待 IO, 間接提高 CPU 占用率,造成耗電。

二、原因分析

1. 圖片顯示原理

圖片其實是由很多個像素點組成的,每個像素點描述了該點的顏色信息。這樣的數(shù)據(jù)是可以被直接渲染在屏幕上的,稱之為 Image Buffer。

事實上,由于圖片源文件占用的存儲空間非常大,一般在存儲時候都會進(jìn)行壓縮,非常常見的就是 JPEG 和 PNG 算法壓縮的圖片。

因此當(dāng)圖片存儲在硬盤中的時候,它是經(jīng)過壓縮后的數(shù)據(jù)。經(jīng)過解碼后的數(shù)據(jù)才能用于渲染,因此需要將圖片顯示在屏幕上的話,需要先經(jīng)過解碼。解碼后的數(shù)據(jù)就是 Image Buffer 。

當(dāng)圖片顯示在屏幕上時,會復(fù)制顯示區(qū)域的Image Buffer去進(jìn)行渲染。

2. 圖片真實占用內(nèi)存

對于一張正在顯示在屏幕上的,尺寸為 1920*1080 的圖片來說,如果采用 SRGB 的格式(每個像素點的顏色由 red,green,blue,alpha 一個共 4 個 bytes 來決定)的話,那么它占用的內(nèi)存為:

也就是說,一張非常普通的圖片,解碼后占用的內(nèi)存就是 7.9 MB,這是非??鋸埖摹6鴪D片顯示時所占的內(nèi)存大小是與尺寸和顏色空間正相關(guān)的,與壓縮算法、圖片格式、圖片文件的大小沒有關(guān)聯(lián)。

三、解決方式

1. 避免將圖片放在內(nèi)存里

對于不顯示在屏幕上的圖片,在絕大部分時間里,其實是沒有必要放在內(nèi)存里的。解碼后的 UIImage 是非常大的,對于不需要顯示的圖片是不需要解碼的。而對于不顯示在屏幕上的圖片,一般也沒有必要繼續(xù)持有著 UIImage 對象。

2.圖片縮放

圖片縮放是很常見的處理方式,一般來說,常見的思想可能是重新畫一張小一點的圖片,往往是用 UIGraphicsBeginImageContextWithOptions的方式:

這種方式存在以下問題:

第一,默認(rèn)是 SRGB 的格式,也就是說每個像素需要占4個bytes的空間,對于一些黑白或者僅有alpha通道的數(shù)據(jù)來說是沒有必要的。

第二,需要將原圖片完全解碼后渲染出來,原圖片的解碼會造成內(nèi)存占用的高峰。

對于問題一的解決,可以使用新的 UIGraphicsImageRenderer 的方式,這種情況下框架會自動幫你選擇對應(yīng)的顏色格式,減少不必要的消耗。

這種方式在一定的場景有所優(yōu)化,但是沒有解決問題二中存在的內(nèi)存峰值的問題。由于處理前的圖片并不一定展示在屏幕上,解碼后的數(shù)據(jù)是冗余信息,因此應(yīng)該避免圖片的解碼。

對于峰值過高的問題,最直接的思想是采用流式的方式進(jìn)行處理。而底層的 ImageIO 的接口就采用了這種方式:

3. 降低峰值

通過 ARC 管理內(nèi)存的對象,注冊在某個 Autoreleasepool 中,Autoreleasepool 在 drain 的時候釋放已經(jīng)沒有使用的對象。

一般沒有進(jìn)行特殊處理的話,會在 Runloop 結(jié)束后,有一次 Autoreleasepool 的 drain 操作,而這次 Runloop 中生成的對象也是由這個 Autoreleasepool 來管理的。這部分的原理有很多的文章介紹,這里就不多贅述了。

在圖片批量處理的過程中,由于還在一個 Runloop 里,此時引用計數(shù)為 0 的對象是不會被釋放的。因此需要在每次循環(huán)后觸發(fā) Autoreleasepool 的 drain 操作:

4. 裁剪顯示的圖片

在很多場景下,圖片是不會完整的顯示出來的,例如下圖所示的情況:

在這種情況中,即使給 UIImageView 一張完整的圖片,最后渲染的時候也只會截取顯示區(qū)域的 Image Buffer 去進(jìn)行渲染。

這就意味著,區(qū)域外的數(shù)據(jù),其實是沒有必要的。因此在這種場景下,其實只需要裁減顯示區(qū)域的圖片即可。

舉個例子,以前面提到 1920 * 1080 的圖片為例, 顯示時需要占用的內(nèi)存為 829440 bytes。如果它是以 ScaleAspectFill 的方式放置在一個 300 x 300 的 UIImageView 中時,那么其實一張 300 x 300 的圖片就足以展示,而此時這張圖片占用的內(nèi)存為 360000 bytes, 僅為前者的 43% 。

關(guān)鍵詞: 減少內(nèi)存占用的

上一篇:鍵盤上怎么打出頓號?

下一篇:電腦聲音與卸載聲卡驅(qū)動有關(guān)嗎?

責(zé)任編輯:

最近更新

點擊排行
推薦閱讀