[Android 效能最佳化系列]記憶體之終極篇--降低你的記憶體消耗

來源:互聯網
上載者:User

標籤:android   記憶體   效能最佳化   

大家如果喜歡我的部落格,請關注一下我的微博,請點擊這裡(http://weibo.com/kifile),謝謝

轉載請標明出處(http://blog.csdn.net/kifile),再次感謝


原文地址:http://developer.android.com/training/articles/memory.html

在接下來的一段時間裡,我會每天翻譯一部分關於效能提升的Android官方文檔給大家

建議大家在看本文之前先去我的部落格看看

[Android 效能最佳化系列]記憶體之基礎篇--Android如何管理記憶體

[Android 效能最佳化系列]記憶體之提升篇--應用應該如何管理記憶體

題外話:

   這一篇文章是Android官網記憶體部分的最後一節,看完了這個記憶體文檔之後,發現很多東西自己以前從來沒有差覺過,比如對象和類所消耗的記憶體空間,依賴庫的使用,多進程的使用等等。希望大家能夠好好看看,多理解一下Android的記憶體機制,如果覺得我翻譯的不夠到位,自己希望看看官網的原文,可以點擊上面的連結去查看。

下面是本次的本文:

################


拒絕在 Bitmap 上浪費你的記憶體

當你載入一張 Bitmap 的時候,你可以選擇只在你的確需要在螢幕上顯示的時候才將它載入到記憶體裡,或者通過縮放原圖的尺寸來減小記憶體佔用。請記住隨著 Bitmap 尺寸的增長,圖片所消耗的記憶體會成平方量級的增長,因為 Bitmap 的 x 軸和 y 軸都在增長。

注意:在 Android2.3及以下的平台,Bitmap 對象在你應用堆中的大小總是一樣的(因為他實際的記憶體被單獨存放在了本地記憶體中)。這會使得我們分析 Bitmap 消耗的記憶體資源變得非常困難,因為大多數分析工具只收集你應用的 Dalvik 堆資訊。但是,從 Android3.0開始,為了提升垃圾收集和調試的能力,Bitmap 的像素資料被放在了你的 Dalvik 堆裡。因此如果你的應用使用了 Bitmap 並且你在老裝置上無法發現應用的記憶體消耗問題,那麼請你在 Android3.0或者更高的機型上調試他。

對於更多使用 Bitmap 的協助,你可以去查看Manage Bitmap Memory(我強烈建議可以去看看,對於降低Bitmap的記憶體佔用很有協助)


使用最佳化後的資料容器

請使用 Andorid 架構中最佳化過的資料容器,例如 SparseArray,SparseBooleanArray 和 LongSparseArray。類似於 HashMap 這一類的容器的效率不是很高,因為在每個 Map 中對於每一次的存放資料,他都需要獨立一個單獨的 Entry 對象進行傳芳。而 SparseArray 由于禁止系統自動封裝鍵值對,因此他更加有效率。並且你不需要擔心丟失掉原有資訊


小心記憶體花銷

請對你正在使用的語言和依賴包擁有一定的瞭解,並且在你設計應用的整個階段,都不要忽視它。通常大多數看起來無害的東西都可能讓你花費大量的記憶體,比如說一下的幾個:

1.枚舉與靜態常量相比,通常會消耗兩倍的記憶體資源,因此你應該盡量避免在 Android 中使用枚舉類型

2.Java 中的每一個類(包括匿名內部類),都會消耗大約500位元記憶體

3.每一個類對象都會消耗12-16位元記憶體

4.把單個 Entry 放入 HashMap 需要多消耗32位元的記憶體(原因請參看上一小節,使用最佳化後的資料容器)

雖然這裡的消耗看起來比較少,但是他們累計起來就很大了,應用中設計那些重量級的類就很可能承受這些記憶體花銷。這會使你的堆分析變得困難起來,你很難發現你的問題其實是因為很多小的對象正在佔用你的記憶體。


小心抽象代碼

通常,開發人員都會將抽象作為一種好的編程習慣,因為抽象可以提升代碼的靈活性和可維護性。但是,抽象方法可能帶來很多的額外花費,例如當他們執行的時候,他們擁有大量的代碼,並且他們會被多次映射到記憶體中佔用更多的記憶體,因此如果抽象的效果不是很好,那麼最好放棄他


對於序列化資料使用 Protobufs

Protocol buffers 是Google一種跨語言,跨平台的結構化序列資料,相比於 XML,他更小,更快,更簡單。如果你決定讓你的資料使用 Protobufs,你應用總是在你的用戶端使用納米級的Protobufs。規則的Protobufs會產生極其冗餘的代碼,這可能會導致應用產生各種問題:增加記憶體使用量,APK包體增加,執行效率變慢,打破Dex的符號限制。

更多的資訊,請參看protobuf readme


避免依賴注入架構

使用類似於 Guice 和 RoboGuice 的依賴注射架構,或許會使你的代碼變得更加漂亮,因為他們能夠減少你需要寫的代碼,並且為測試或者在其他條件改變的情況下,提供一種自適應的環境。但是,這些架構在初始化的時候會因為注釋而消耗大量的工作在掃描你的代碼上,這會讓你的代碼在進行記憶體映射的時候花費更多的資源。雖然這些記憶體能夠被 Android 進行回收,但是等待整個分頁被釋放需要很長一段時間。


小心使用外部依賴包

很多依賴包都不是專門為了移動環境或者移動用戶端寫的。如果你決定使用一個外部依賴包,你應該提前明白你需要為了將它移植到移動端而消耗花費大量的時間和工作量。請在使用外部依賴包得時候提前分析他的代碼和記憶體佔用

即使依賴包是為了 Android 而設計的,但是這也有潛伏的危險,因為每一個包都做著不同的工作。例如,有一個依賴包使用納米級的 protobufs 但是別的包使用微米級的 protobufs.那麼現在在你的應用中就有兩套 protobuf 的標準了。這會在你記錄資料,分析資料,載入映像,緩衝,或者其他任何可能的情況下發生你不希望發生的事情。ProGuard 無法在這裡協助你,因為他們都是你所依賴包的底層實現,。當你使用從別的依賴包(他可能繼承了很多的依賴包)裡繼承的 Activity 時,這個問題變得尤其嚴重,當你使用反射以及幹別的事情的時候

請注意不要落入一個依賴包的陷阱,你不希望引入一大片你根本不會使用到的代碼。如果你無法找到一種已經實現的邏輯來完全滿足你的需求,那麼你盡量建立一個自己的實現方式。


最佳化整體效能

很多關於最佳化應用的整體效能的資訊被放到了 Best Practices for Performance,這裡的很多文章介紹了如何最佳化 CPU 效能,但是很多小提示能夠協助你最佳化你的應用的記憶體使用量,比如說通過減少你ui 的布局元素。

你應該讀一下最佳化你的 ui,並使用布局調試工具來進行最佳化,另外可以通過 lint 工具來進行最佳化


使用 ProGuard 來剔除你不用的代碼

ProGuard 工具能夠通過移除不用的代碼以及對類,方法和標量進行無意義的重新命名來起到回收,最佳化和混淆代碼的作用,使用 ProGuard 能夠使你的代碼變得更緊湊,而且減低記憶體消耗


使用 Zipalign 來最佳化你的 Apk

如果你對你產生的 APK 檔案做了後期最佳化,那麼你必須要使用 Zipalign 來讓他對齊位元組。不這樣做可能會導致你的應用因為從 APK 裡的資源不能被很好的映射到記憶體裡而消耗更多的記憶體。

注意:現在 Google 市場不接受沒有通過 Zipalign 處理過的 APK 檔案


分析你的記憶體使用量

一旦你已經擁有一個穩定版本的應用,那麼就從他的整個生命週期開始分析你應用的使用記憶體。更多關於你應用的記憶體分析,請查看Investigating Your RAM Usage.


使用多進程

你應該在恰當的時候將你的應用組件分布到多個進程中,這能夠協助你管理你的應用記憶體。你應該被小心的使用這個技術,而且絕大部分應用都沒有必要在多線程中運行。如果錯誤的使用,反而可能導致你的應用消耗更多的記憶體,而不是減少。

舉個例而言,你可以在構建一個音樂播放器的時候使用多進程,因為他的後台服務會持續很長一段時間,當整個應用運行在一個進程中,他的 activity 介面上的那些資源在播放音樂的時候還會被儲存,即使這個時候使用者已經去到了另外一個應用,但是你的服務還在播放音樂。這樣的一個應用應該獨立的使用兩個進程,一個用於 ui 線程,另一個用於執行後台服務。

你可以通過在 manifest 檔案中為組件定義 android:process 屬性來為他們區分所屬的進程。例如,你可以讓你的某個服務運行在獨立於主線程的進程中,通過定義一個進程明教“backgroud”,這個名字可以隨便定義

<service android:name=".PlaybackService"         android:process=":background" />

你的進程的名稱應該以一個逗號開始,以確保這個進程是你應用私人的

在你決定建立一個新進程之前,你需要理解記憶體的消耗情況。為了說明每個進程的重要性,我們來看看一個空進程,即使他什麼事情也不做,他也會消耗1.4M 的空間,就像下面的記憶體資訊裡顯示的那樣:

adb shell dumpsys meminfo com.example.android.apis:empty** MEMINFO in pid 10172 [com.example.android.apis:empty] **                Pss     Pss  Shared Private  Shared Private    Heap    Heap    Heap              Total   Clean   Dirty   Dirty   Clean   Clean    Size   Alloc    Free             ------  ------  ------  ------  ------  ------  ------  ------  ------  Native Heap     0       0       0       0       0       0    1864    1800      63  Dalvik Heap   764       0    5228     316       0       0    5584    5499      85 Dalvik Other   619       0    3784     448       0       0        Stack    28       0       8      28       0       0    Other dev     4       0      12       0       0       4     .so mmap   287       0    2840     212     972       0    .apk mmap    54       0       0       0     136       0    .dex mmap   250     148       0       0    3704     148   Other mmap     8       0       8       8      20       0      Unknown   403       0     600     380       0       0        TOTAL  2417     148   12480    1392    4832     152    7448    7299     148

注意,關於如何閱讀這些輸出的資訊,可以查看 Investigating Your RAM Usage.,這裡主要的內容是 Private Dirty 和 Private Clean 這兩塊,他們分別顯示了這個進程使用了大約1.4M 的未分頁記憶體(分別分布在 Dalvik 堆,本地分配空間,類庫載入),此外還有大約150k 的記憶體被映射到了記憶體執行。

這個空進程的記憶體輸出情況是相當清楚的。當你在進程中進行操作時,進程的記憶體還會繼續增大。舉個例子,這裡是一個只在 activity 上顯示了一些文本的進程的記憶體資料:

** MEMINFO in pid 10226 [com.example.android.helloactivity] **                Pss     Pss  Shared Private  Shared Private    Heap    Heap    Heap              Total   Clean   Dirty   Dirty   Clean   Clean    Size   Alloc    Free             ------  ------  ------  ------  ------  ------  ------  ------  ------  Native Heap     0       0       0       0       0       0    3000    2951      48  Dalvik Heap  1074       0    4928     776       0       0    5744    5658      86 Dalvik Other   802       0    3612     664       0       0        Stack    28       0       8      28       0       0       Ashmem     6       0      16       0       0       0    Other dev   108       0      24     104       0       4     .so mmap  2166       0    2824    1828    3756       0    .apk mmap    48       0       0       0     632       0    .ttf mmap     3       0       0       0      24       0    .dex mmap   292       4       0       0    5672       4   Other mmap    10       0       8       8      68       0      Unknown   632       0     412     624       0       0        TOTAL  5169       4   11832    4032   10152       8    8744    8609     134

這個進程現在的大小差不多有4M,但是他真的只在 ui 介面上顯示了一些文本。通過這個,我們可以得出結論,如果你打算將你的應用分割到多進程中,那麼只能讓一個進程響應 ui,其他進程應該避免響應 ui,否則會極大的提升記憶體佔用,尤其是當你載入了 bimap 或者其他一些資源的時候。這會導致 ui 繪製完成之後,我們也很難甚至於說不可能降低記憶體消耗。

除此之外,當運行超過一個進程的時候,你應該儘可能讓你的代碼保持簡潔。因為那些不必要的記憶體。例如說,如果你使用了枚舉(我們在之前討論過枚舉的記憶體消耗大約是靜態常量的兩倍),那麼所有的進程中 RAM 都需要建立並且初始化這些常量,而且你在局部地區或者適配器中使用過的抽象方法也會被反覆複製。

多進程的另一個核心是兩者之間的依賴關係,比如說,如果你的應用中有一個運行在預設進程上的內容提供者,那麼在後台進程中的代碼如果想要訪問這個內容提供者,就需要在 ui 線程中。如果你的目標是避免在繁重的 ui 線程中執行操作,那麼你就不應該依賴處於 ui 線程中的內容提供者或者服務。


##########我是分隔字元#######################

Ok,關於Android的記憶體,就翻譯到這裡了,之後我會將更多的官網文章翻譯過來,希望大家 喜歡,如果點贊就更好了

[Android 效能最佳化系列]記憶體之終極篇--降低你的記憶體消耗

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.