国产成人精品久久免费动漫-国产成人精品天堂-国产成人精品区在线观看-国产成人精品日本-a级毛片无码免费真人-a级毛片毛片免费观看久潮喷

您的位置:首頁技術文章
文章詳情頁

詳解android是如何管理內存的

瀏覽:2日期:2022-09-20 09:06:32
前言

很高興遇見你~

內存優化一直是 Android 開發中的一個非常重要的話題,他直接影響著我們 app 的性能表現。但這個話題涉及到的內容很廣且都偏向底層,讓很多開發者望而卻步。同時,內存優化更加偏向于“經驗知識”,需要在實際項目中去應用來學習。

因而本文并不想深入到底層去講內存優化的原理,而是著眼于宏觀,聊聊 android 是如何分配和管理內存、在內存不足的時候系統會如何處理以及會對用戶造成什么樣的影響。

Android 應用基于 JVM 語言進行開發,雖然 google 根據移動設備特點開發了自家的虛擬機如 Dalvik、ART,但依舊是基于 JVM 模型,在堆區分配對象內存。因此 Java heap(java 堆)是android應用內存分配和回收的重點。其次,移動設備的 RAM 非常有限,如何為進程分配以及管理內存也是重中之重。

文章的主要內容是分析 Java heap、RAM 的內存管理,以及當內存不夠時 android 會如何處理。

那么,我們開始吧。

Java Heap

Java Heap,也就是 JVM 中的堆區。簡單回顧一下 JVM 中運行時數據區域的劃分:

詳解android是如何管理內存的

橙色區域的方法棧以及程序計數器屬于線程私有,主要存儲方法中的局部數據。 方法區主要存儲常量以及類信息,線程共享。 堆區主要負責存儲創建的對象,幾乎一切對象的內存都在堆區中分配,同時也是線程共享。

我們在 android 程序中使用如 Object o = new Object() 代碼創建的對象都會在堆區中分配一塊內存進行存儲,具體如何分配由虛擬機解決而不需要我們開發者干預。當一個對象不再使用時, JVM 中具有垃圾回收機制(GC),會自動釋放堆區中無用的對象,重新利用內存。當我們請求分配的內存已經超過堆區的內存大小,則會拋出 OOM 異常。

在 android 中,堆區是一個由 JVM 邏輯劃分的區域,他并不是真正的物理區域。堆區并不會直接全部映射和他等量大小的物理內存,而是到了需要使用時,才會去建立邏輯地址和物理地址的映射:

詳解android是如何管理內存的

這樣可以給應用分配足夠的邏輯內存大小,同時也不必在啟動時一次性分配一大塊的物理內存。在相同大小的內存中,可以運行更多的程序。

當堆區進程 GC 之后,釋放出來多余的空閑內存,會返還給系統,減少物理內存的占用。但這個過程涉及到比較復雜的系統調用,若釋放的內存較為少量,可能得不償失,則無需返還給系統,在堆區中繼續使用即可。

在 GC 過程中,如果一個對象不再使用,但是其所占用的內存無法被釋放,導致資源浪費,這種現象稱為內存泄漏。內存泄露會導致堆區中的對象越來越多,內存的壓力越來越大,甚至出現 OOM 。因此,內存泄露是我們必須要盡量避免的現象。

進程內存分配

堆區的內存分配,屬于進程內的內存分配,由進程自己管理。下面講一個應用,系統是如何為其分配內存的。

系統的運行內存,即為我們常說的 RAM ,是應用的運行空間。每個應用必須裝入內存中才可以被執行:

詳解android是如何管理內存的

我們安裝的應用進程都位于硬盤中 當一個應用被執行時,需要裝入到 RAM 中才能被執行(zRAM 是為了壓縮數據節省空間而設計,后續會講到) CPU 與 RAM 交互,讀取指令、數據、寫入數據等

RAM 的大小為設備的硬件內存大小,是非常寶貴的資源。現代手機常見的運存是6G、8G或者12G,一些專為游戲研發的手機甚至有18G,但同時價格也會跟上去。

Android 采用分頁存儲的方式把一個進程存儲到 RAM 中。分頁存儲,簡單來說就是把內存分割成很多個小塊,每個應用占用不同的小塊,這些小塊也可以稱為頁:

詳解android是如何管理內存的

前面講到,進程的堆區并不是一次性分配,當需要分配內存時,系統會為其分配空閑的頁;當這些頁被回收,那么有可能被返還到系統中。

這里的頁、塊概念涉及到操作系統的分頁存儲,這里并不打算展開詳細講解,有興趣的讀者可以自行了解:分頁存儲-維基百科。本文中的“頁”與“塊”可以不嚴謹地理解為同個概念,為了幫助理解這里不進行詳細地區分。

分配給進程的頁可以分為兩種類型:干凈頁、臟頁:

干凈頁:進程從硬盤中讀取數據或申請內存之后未進行修改。這種類型的頁面在內存不足的時候可以被回收,因為頁中存儲的數據可通過其他的途徑復原。 臟頁:進程對頁中的數據進行了修改或數據存儲。這類頁面不能被直接回收,否則會造成數據丟失,必須先進行數據存儲。

zRAM,是作為 RAM 中的一個分區,當內存不足時,可以把一些類型的頁壓縮之后存儲在zRAM中,當需要使用的時候再從zRAM中調出。通過壓縮來節省應用的空間占用,同時不需要與硬盤進行調度,提高了速度。

這里需要理解的一個點是:內存中的操作速度要遠遠比硬盤操作快。即使與zRAM的調入和調出需要壓縮和解壓,其速度也是比與硬盤交互快得多。

內存不足管理

前面我們一直強調,移動設備的內存容量是非常有限的,需要我們非常謹慎地去使用它。幸運的是,JVM 和 android 系統早就幫我們想到了這一點。

面對不同的內存壓力,android 會有不同的應對策略。從低到高依次是 GC、內核交換守護進程釋放內存、低內存終止守護進程殺死進程釋放內存;他們的代價也是逐步上升。下面我們依個來介紹一下。

GC 垃圾回收

GC 屬于 JVM 內部的內存管理機制,他管理的內存區域是堆區。當我們創建的對象越來多,堆區的壓力越來越大時,GC 機制就會啟動,開始回收堆區中的垃圾對象。

辨別一個對象是否是垃圾,虛擬機采用的是可達性分析法。即從一些確定活躍有用的對象出發,向下分析他的引用鏈;如果一個對象直接或者間接這些對象所引用,那么他就不是垃圾,否則就是垃圾。這些確定活躍有用的對象稱為 GC Roots:

詳解android是如何管理內存的

如上圖,其中綠色的對象被 GC Roots 直接或間接引用,則不會被回收;灰色的對象沒有被引用則被標記為垃圾GC Roots對象的類型比較常見的是靜態變量以及棧中的引用。靜態變量比較好理解,他在整個進程的執行期間不會被回收,因此他肯定是有用的。棧,這里指的是 JVM 運行數據區域中的方法棧,也就是局部變量引用,在方法執行期間肯定是活躍的。由于方法棧屬于線程私有,因此這里等于活躍線程持有的對象不會被回收。

因此,如果一個對象對于我們的程序不再使用,則必須解除 GC Roots 對其的引用,否則會造成內存泄露。例如,不要把 activity 賦值給一個靜態變量,這樣會導致界面退出時activity無法被回收。

GC 也并不是直接對整個堆區進行回收,而是將堆區中的對象分成兩個部分:新生代、老年代。

剛創建的對象大都會被回收,而在多次回收中存活的對象則后續也很少被回收。新生代中存儲的對象主要是剛被創建不久的對象,而老年代則存儲著那些在多次 GC 中存活的對象。那么我們可以針對這些不同特性的對象,執行不同的回收算法來提高GC性能:

對于新創建的對象,我們需要更加頻繁地對他們進行GC來釋放內存,且每次只需要記錄需要留下來的對象即可,而不必要去標記其他大量需要被回收的對象,提高性能。 對于熬過很多次GC的對象,則可以以更低的頻率對他門進行GC,且每次只需要關注少量需要被回收的對象即可。

具體的垃圾回收算法就不繼續展開了,了解到這里就可以。感興趣的讀者可以閱讀相關書籍。

單次的垃圾回收速度是很快的,甚至我們都無法感知到。但當內存壓力越來越大,垃圾回收的速度跟不上內存分配的速度,此時就會出現內存分配等待 GC 的情況,也就是發生了卡頓。同時,我們無法控制 GC 的時機,JVM 有一套完整的算法來決定什么時候進行 GC。假如在我們滑動界面的時候觸發 GC ,那么展示出來的就是出現了掉幀情況。因此,做好內存優化,對于 app 的性能表現非常重要。

內核交換守護進程

GC 是針對于 Java 程序內部進行的優化。對于移動設備來說,RAM 非常寶貴,如何在有限的 RAM 資源上進行分配內存,也是一個非常重要的話題。

我們的應用程序都運行在 RAM 中,當進程不斷申請內存分配,RAM 的剩余內存達到一定的閾值時,會啟動內核交換守護進程來釋放內存以滿足資源的分配。

內核交換守護進程,是運行在系統內核的一個進程,他主要的工作時回收干凈頁、壓縮頁等操作來釋放內存。前面講到,android 是基于分頁存儲的操作系統,每個進程都會被存儲到一些頁中。分頁的類型有兩種:干凈頁、臟頁:

當內核交換守護進程啟動時,他會把干凈頁回收以釋放內存。當進程再次訪問干凈頁時,則需要去硬盤中再次讀取。 對于臟頁,內核交換守護進程會把他們壓縮后放入 zRAM 中。當進程訪問臟頁時,則需要從zRAM中解壓出來。

通過不斷回收和壓縮分頁的方式來釋放內存,以滿足新的內存請求。使用此方式釋放的內存也無法滿足新的內存請求時,android 會啟動低內存終止守護進程,來終止一些低優先級的進程。

低內存終止守護進程

當 RAM 的被占用內存達到一定的閾值,android 會根據進程的優先級,終止部分進程來釋放內存。當低內存終止守護進程啟動時,說明系統的內存壓力已經非常大了,這在一些性能較差的設備中經常出現。

進程的優先級從高到低排序如下,優先級更高的進程會優先被終止:

詳解android是如何管理內存的

圖片來源:developer.android.google.cn/topic/perfo…

從上到下依次是:

后臺應用:使用過的 app 會被緩存在后臺,下一次打開可以更加快速地進行切換。當內存不足時,此類應用會最快被殺死。 上一個應用:例如從微信跳轉到瀏覽器,此時微信就是上一個應用。 主屏幕應用:這是啟動器應用,也就是我們的桌面。如果這個進程被kill了,那么返回桌面時會暫時黑屏。 服務:同步服務、上傳服務等等 可覺察的應用:例如正在播放的音樂軟件,他可以被我們感知到,但是不在前臺。 前臺應用:當前正在使用的應用,如果這個應用被kill了,需要向用戶報崩潰異常,此時的體驗是極差的。 持久性(服務):這些是設備的核心服務,例如電話和 WLAN。 系統:系統進程。這些進程被終止后,手機可能即將重新啟動,就像手機突然卡死重啟。 原生:系統使用的極低級別的進程,例如我們的內核交換守護進程。

當內存不足,會按照上面的規則,從上到下來終止進程,獲得內存資源。這也就是為什么在 android 中我們的后臺應用一直被殺死。為了避免我們的應用被優化,內存優化就顯得非常重要了。

最后再來回顧一下:

詳解android是如何管理內存的

圖片來源:www.youtube.com/watch?v=w7K…

在0-1階段,系統的內存資源足夠,程序請求內存分配,系統會不斷地使用空閑頁來滿足應用的內存請求 在1-2階段,系統的可利用內存下降到一個閾值,程序繼續請求內存分配,內核交換守護進程啟動,開始釋放緩存來滿足內存請求 在2-3階段,系統的被利用內存達到一個閾值,系統將啟動低內存終止守護進程來殺死進程釋放內存 最后

我們文章分析了 android 是如何對內存進行分配以及低內存時如何釋放內存來滿足內存請求。可以很明顯看到,當內存不足時,會嚴重影響我們 app 的體驗甚至整個用戶手機的體驗:

當內存不足會造成頻繁GC、回收干凈頁、回寫緩存,導致應用緩慢、卡頓 如果設備內存一直不夠,那么會一直殺死進程影響用戶體驗,特別是這些進程是用戶非常在意的如游戲、微信 內存占用過高會讓app在后臺被殺死、或者讓用戶的其他app被殺死、甚至整個系統無法運行而直接崩潰重啟, 不是所有的設備都有著高內存,有著設備只有很少的內存,在一些性能較差的設備上甚至會無法運行,這樣我們就失去了這些設備的市場

反觀現在國內的很多 app,有如扣扣、t寶、iqy,在我這個三年前的機器上運行會發生嚴重卡頓,偶爾還有ANR崩潰的出現;而當我去測試了youto、tele、Twit等 app ,發現基本不會發生卡頓,甚至在 youto 這樣有大量圖片視頻加載的 app 界面切換也盡享絲滑。這兩種 app 的體驗是有著天壤之別的。

本文沒有講如何進行內存優化,是因為這一塊的內容設計到的太廣太深,無法在這篇文章中一并介紹。文章的目的只是為了幫助讀者了解android是如何管理內存以及內存不足可能造成的后果,對內存的重要性能有一個感性的認知。

以上就是詳解android是如何管理內存的的詳細內容,更多關于Android 管理內存的資料請關注好吧啦網其它相關文章!

以上就是詳解android是如何管理內存的的詳細內容,更多關于Android 管理內存的資料請關注好吧啦網其它相關文章!

標簽: Android
相關文章:
主站蜘蛛池模板: 亚洲视频在线观看视频 | 久草看片 | 欧美国产日韩在线 | 国内自拍在线视频高清 | 日韩三级小视频 | 一级黄色欧美片 | 在线 | 一区二区三区四区 | 久久99视频 | 欧美性f| 好吊操这里只有精品 | 色噜噜国产精品视频一区二区 | 美女一级视频 | 寡妇一级a毛片免费播放 | 99re在线视频精品 | 91在线产啪 | 国产欧美一区二区精品久久久 | 日韩精品久久一区二区三区 | 免费人成观看在线网 | 一级黄一片 | 午夜影院啪啪 | 国产精品久草 | 亚洲欧美日韩国产精品久久 | 一级特黄特黄毛片欧美的 | 亚洲偷| 精品国产v无码大片在线观看 | 日韩三级在线免费观看 | 免费一区在线观看 | 在线免费公开视频 | 国产在线乱子伦一区二区 | 99久久综合国产精品免费 | 久爱免费观看在线网站 | 一级一毛片a级毛片欧美 | 精品动漫一区二区 | 久久精品一区二区免费看 | 男吃女下面刺激视频免费 | 久久国产网 | 日本苍井一级毛片 | 99视频在线观看免费视频 | 国产精选91热在线观看 | 欧美国产精品不卡在线观看 | 久久国产成人亚洲精品影院老金 |