安卓效能最佳化 視頻

來源:互聯網
上載者:User

標籤:ondraw   工具   總結   剖析   面試題   oca   一個   init   記憶體   

 韓夢飛沙  韓亞飛  [email protected]  yue31313  han_meng_fei_sha

 

 

安卓-效能最佳化之記憶體流失-原創-高清視頻-愛奇藝

=========

Android手機記憶體管理與效能最佳化視頻教程下載

下載:http://pan.baidu.com/s/1skkSdI1 密碼:kdcs

 

01.Dalvik介紹及其優勢和檔案格式

02.基於棧與基於寄存器比較

03.DEX與ODEX檔案格式和ODEX化詳解

04.GC記憶體回收機制

05.記憶體監測分析工具DDMS介紹

06.記憶體配置跟蹤工具DDMS_Alocation Tracker詳解

07.監測工具與分析工具介紹及File Explorer

08.釋放對象的引用和Context的使用

09.Adapter和圖片處理及迴圈變數使用不當

10.查詢資料庫和Static關鍵字使用及線程

11.自身方法和虛擬靜態比較及Final常量

12.圖片處理最佳化和使用類庫及finally塊

13.Android4.4新特性Dalvik被ART替代及本章企業面試題

14.JNI的原理詳解

15.JNI的架構詳解

16.NDK與JNI關係描述和NDK開發環境的搭建

17.NDK配置環境編譯工程並匯入Eclipse運行

18.安裝外掛程式和JNI環境配置及NDK開發流程

19.JNI的優勢與弊端和如何恰當使用JNI

20.Java如何講資料傳遞給C和C回調Java的方法

21.C訪問Java代碼啟動並執行機制剖析

22.JNIEnv指標的作用和設計思路以及設計優勢

23.獲得JNIEnv指標和jobject與java繼承關係及jobject作用

24.講師推薦JNI和NDK文章 線上  

25.獲得java類的class對象樣本詳解

26.訪問Java中的方法與屬性和sign簽名

27.本章內容小結 線上  

28.Java字串中的五組常用方法詳解一

29.Java字串中的五組常用方法詳解二

30.基本類型的數組和物件類型的數組

31.用於兩種不用類數組和全域與局部及弱全域的引用

32.緩衝處理和異常基本處理及異常機制流程

33.CC++和Android的線程模式和Java中調用CC++代碼

34.CC++中多線程調用Java代碼及總結

=========

1. Memory Leak

記憶體流失:對於Java來說,就是new出來的Object 放在Heap上無法被GC回收(記憶體中存在無法被回收的對象);記憶體流失發生時的主要表現為記憶體抖動,可用記憶體慢慢變少。

1.1 Memory Monitor

AndroidStudio內建的Memory Monitor可以方便的觀察堆記憶體的分配情況,並且可以粗略的觀察有沒有Memory Leak。

頻繁的記憶體抖動,可能存在記憶體流失

A:initiate GC 手動觸發GC操作;

B:Dump Java Heap 擷取當前的堆棧資訊,產生一個.hprof檔案,AndroidStudip會自動使用HeapViewer開啟;一般用於操作之後檢測記憶體流失的情況;

C:Start Allocation Tracking 記憶體配置追蹤工具,用於追蹤一段時間的記憶體配置使用方式,能夠知道執行一些列操作後,有哪些對象被分配空間。一般用於追蹤某項操作之後的記憶體配置,調整相關的方法調用來最佳化app效能與記憶體使用量;

D:剩餘可用記憶體;

E:已經使用的記憶體。

點擊Memory Monitor的Dump Java Heap,會產生一個.hprof檔案,AndroidStudio會自動使用HeapViewer開啟。

Hprof Viewer開啟.hprof檔案

左面板說明:

  • Total Count 該類的執行個體個數
  • Heap Count 選定的Heap中執行個體的個數
  • Sizeof 每個執行個體佔用的記憶體大小
  • Shallow Size 所有該類的執行個體佔用的記憶體大小
  • Retained Size 該類的所有執行個體可支配的記憶體大小

右面板說明:

  • Instance 該類的所有執行個體對象(左側Total Count為15,此處就有15個對象)
  • Depth 深度, GC Root點到該執行個體的最短鏈路數
  • Dominating Size 該執行個體可支配的記憶體大小

此處可以看出MainActivity存在了15個樣本對象,懷疑此處有問題。

1.2 MAT

上述只是可以粗略的看出是不是有問題,而要知道問題出在哪裡就需要藉助MAT了。將產生的.hprof檔案進行轉換,然後使用MAT開啟;

格式轉換命令:hprof-conv 原檔案路徑 轉換後檔案路徑

MAT開啟.hprof

注意下面的Actions:

  • Histogram可以列出記憶體中每個對象的名字、數量以及大小。
  • Dominator Tree會將所有記憶體中的對象按大小進行排序,並且我們可以分析對象之間的引用結構。

一般使用最多的也是這兩個功能。

Retained Heap表示這個對象以及它所持有的其它引用(包括直接和間接)所佔的總記憶體

  • 使用Histogram:
  • 點擊Histogram並在頂部的Regex中輸入MainActivity會進行正則匹配,會將包含“MainActivity”的所有對象全部列出了出來,其中第一行就是MainActivity的執行個體。 

  • 對著想查看的對象點擊右鍵 -> List objects -> with incoming references 查看具體MainActivity執行個體。 

  • 對想要查看的對象執行個體點擊右鍵-> Path To Gc Roots -> exclude weak reference(排除掉軟引用)。 

注意:

this$0前面的表徵圖的左下角有個圓圈,這代表這個引用可以被Gc Roots引用到,由於MainActivity$LeakClass能被GC Roots訪問到導致其不能被回收,從而它所持有的其它引用也無法被回收了,包括MainActivity,也包括MainActivity中所包含的其它資源。

此時我們就找到了記憶體流失的原因。

  • 使用Dominator Tree 

使用上面Histogram的操作方式也可以找到泄漏的具體原因,此處不再累述。

注意:每個對象前的表徵圖的圓圈,並不代表一定是導致記憶體流失的原因,有些對象就是需要在記憶體中存活的,需要區別對待。

1.3 LeakCanary

LeakCanary是square出品的一個檢測記憶體流失的庫,整合到App之後便無需關心,在發生記憶體流失之後會Toast、通知欄彈出等方式提示,可以指出泄漏的引用路徑,而且可以抓取當前的堆棧資訊供詳細分析。

2. Out Of Memory

2.1 Android OOM

Android系統的每個進程都有一個最大記憶體限制,如果申請的記憶體資源超過這個限制,系統就會拋出OOM錯誤。

  • Android 2.x系統,當dalvik allocated + external allocated + 新分配的大小 >= dalvik heap 最大值時候就會發生OOM。其中bitmap是放於external中 。
  • Android 4.x系統,廢除了external的計數器,類似bitmap的分配改到dalvik的java heap中申請,只要allocated + 新分配的記憶體 >= dalvik heap 最大值的時候就會發生OOM(art運行環境的統計規則還是和dalvik保持一致)

記憶體溢出是程式運行到某一階段的最終結果,直接原因是剩餘的記憶體不能滿足記憶體的申請,但是再分析間接原因記憶體為什麼沒有了:

  • 記憶體流失的存在可能導致可用記憶體越來越少;
  • 記憶體申請的峰值超過了系統時間點剩餘的記憶體;(例如:某手機單個進程可用最大記憶體為192M,目前分配記憶體80M,此時申請5M記憶體,但是目前時間點整個系統可用記憶體只有3M,此時沒有超出單個進程可用最大記憶體,但是OOM也會發生)

2.2 Avoid Android OOM

除了避免記憶體流失之外,根據《Manage Your App‘s Memory》,我們可以對記憶體的狀態進行監聽,在Activity中覆寫此方法,根據不同的case進行不同的處理:

  1. @Override 
  2.     public void onTrimMemory(int level) {        super.onTrimMemory(level); 
  3.     }  

TRIM_MEMORY_RUNNING_MODERATE:你的應用正在運行並且不會被列為可殺死的。但是裝置此時正運行於低記憶體狀態下,系統開始觸發殺死LRU Cache中的Process的機制。

TRIM_MEMORY_RUNNING_LOW:你的應用正在運行且沒有被列為可殺死的。但是裝置正運行於更低記憶體的狀態下,你應該釋放不用的資源用來提升系統效能。

TRIM_MEMORY_RUNNING_CRITICAL:你的應用仍在運行,但是系統已經把LRU Cache中的大多數進程都已經殺死,因此你應該立即釋放所有非必須的資源。如果系統不能回收到足夠的RAM數量,系統將會清除所有的LRU緩衝中的進程,並且開始殺死那些之前被認為不應該殺死的進程,例如那個包含了一個運行態Service的進程。

當應用進程退到後台正在被Cached的時候,可能會接收到從onTrimMemory()中返回的下面的值之一:

TRIM_MEMORY_BACKGROUND: 系統正運行於低記憶體狀態並且你的進程正處於LRU緩衝名單中最不容易殺掉的位置。儘管你的應用進程並不是處於被殺掉的高危險狀態,系統可能已經開始殺掉LRU緩衝中的其他進程了。你應該釋放那些容易恢複的資源,以便於你的進程可以保留下來,這樣當使用者回退到你的應用的時候才能夠迅速恢複。

TRIM_MEMORY_MODERATE: 系統正運行於低記憶體狀態並且你的進程已經已經接近LRU名單的中部位置。如果系統開始變得更加記憶體緊張,你的進程是有可能被殺死的。

TRIM_MEMORY_COMPLETE: 系統正運行於低記憶體的狀態並且你的進程正處於LRU名單中最容易被殺掉的位置。你應該釋放任何不影響你的應用恢複狀態的資源。

3. Memory Churn

Memory Churn記憶體抖動:大量的對象被建立又在短時間內馬上被釋放。

瞬間產生大量的對象會嚴重佔用Young Generation的記憶體地區,當達到閥值,剩餘空間不夠的時候,也會觸發GC。系統花費在GC上的時間越多,進行介面繪製或流音頻處理的時間就越短。即使每次分配的對象佔用了很少的記憶體,但是他們疊加在一起會增加Heap的壓力,從而觸發更多其他類型的GC。這個操作有可能會影響到幀率,並使得使用者感知到效能問題。

Drop Frame Occur

常見的可能引發記憶體抖動的情形:

  • 迴圈中建立臨時對象;
  • onDraw中建立Paint或Bitmap對象等;

例如之前使用過的有些下拉重新整理控制項的實現方式,在onDraw中建立Bitmap等多個臨時大對象會導致記憶體抖動。

4. Bitmap

Bitmap的處理也是Android中的一個痛點,當然使用第三方架構的話就屏蔽掉了這個痛點。

  • Bitmap的記憶體模型;
  • Bitmap的載入、壓縮、緩衝等策略;
  • 版本的相容等;

關於Bitmap之後會寫專門的一篇文章來介紹,此處可以參考《Handling Bitmaps》。

5. Program Advice

5.1 節制地使用Service

記憶體管理最大的錯誤之一就是讓Service一直運行。在後台使用service時,除非它需要被觸發並執行一個任務,否則其他時候Service都應該是停止狀態。另外需要注意Service工作完畢之後需要被停止,以免造成記憶體流失。

系統會傾向於保留有Service所在的進程,這使得進程的運行代價很高,因為系統沒有辦法把Service所佔用的RAM空間騰出來讓給其他組件,另外Service還不能被Paged out。這減少了系統能夠存放到LRU緩衝當中的進程數量,它會影響應用之間的切換效率,甚至會導致系統記憶體使用量不穩定,從而無法繼續保持住所有目前正在啟動並執行service。

建議使用JobScheduler,而盡量避免使用持久性的Service。還有建議使用IntentService,它會在處理完交代給它的任務之後儘快結束自己。

5.2 使用最佳化過的集合

Android API當中提供了一些最佳化過後的資料集合工具類,如SparseArray,SparseBooleanArray,以及LongSparseArray等,使用這些API可以讓我們的程式更加高效。傳統Java API中提供的HashMap工具類會相對比較低效,因為它需要為每一個索引值對都提供一個對象入口,而SparseArray就避免掉了基礎資料型別 (Elementary Data Type)轉換成對象資料類型的時間。

5.3 謹慎對待面向抽象

開發人員經常把抽象作為好的編程實踐,因為抽象能夠提升代碼的靈活性與可維護性。然而,抽象會導致一個顯著的開銷:面向抽象需要額外的代碼(不會被執行到),同樣會被諮映射到記憶體中,耗費了更多的時間以及記憶體空間。因此如果面向抽象對你的代碼沒有顯著的收益,那你應該避免使用。

例如:使用枚舉通常會比使用靜態常量要消耗兩倍以上的記憶體,在Android開發當中我們應當儘可能地不使用枚舉。

5.4 使用nano protobufs序列化資料

Protocol buffers是Google為序列化資料設計的一種語言無關、平台無關、具有良好擴充性的資料描述語言 (Data Description Language),與XML類似,但是更加輕量、快速、簡單。如果使用protobufs來實現資料的序列化及還原序列化,建議在用戶端使用nano protobufs,因為通常的protobufs會產生冗餘代碼,會導致可用記憶體減少,Apk體積變大,運行速度減慢。

5.5 避免記憶體抖動

記憶體回收通常不會影響應用的表現,但是短時間內多次的記憶體回收會消耗掉介面繪製的時間。系統花費在GC上的時間越多,進行介面繪製或流音頻處理的時間就越短。通常記憶體抖動會導致多次的GC,實踐中記憶體抖動代表了一段時間內分配了臨時對象。

例如:在For迴圈中分配了多個臨時對象,或在onDraw()方法中建立了Paint、Bitmap對象,應用產生了大量的對象;這會很快耗盡young generation的可用記憶體,導致GC發生。

使用Analyze your RAM usage中的工具找出代碼裡記憶體抖動的地方。考慮把操作移出內部迴圈,或者將其移動到基於工廠的分配結構中。

5.6 移除消耗記憶體的庫、縮減Apk的大小

查看Apk的大小,包括三方庫和內嵌的資源,這些都會影響應用消耗的記憶體。通過減少冗餘、非必須或大的組件、庫、圖片、資源、動畫等,都可以改善應用的記憶體消耗。

5.7 使用Dagger 2進行依賴注入

如果您打算在應用程式中使用依賴注入架構,請考慮使用Dagger 2。 Dagger不使用反射來掃描應用程式的代碼。 Dagger的編譯時間註解技術實現意味著它不需要不必要的運行時成本。而使用反射的其它依賴注入架構通常通過掃描碼來初始化過程。 此過程可能需要顯著更多的CPU周期和RAM,並可能導致應用程式啟動時明顯的卡頓。

備忘:之前的文檔是不建議使用依賴注入架構,因為實現原理是使用反射,而進化為編譯時間註解之後,就不再有反射帶來的影響了。

5.8 謹慎使用第三方庫

很多開源的library代碼都不是為移動端而編寫的,如果運用在行動裝置上,並不一定適合。即使是針對Android而設計的library,也需要特別謹慎,特別是在你不知道引入的library具體做了什麼事情的時候。例如,其中一個library使用的是nano protobufs, 而另外一個使用的是micro protobufs。這樣一來,在你的應用裡面就有2種protobuf的實現方式。這樣類似的衝突還可能發生在輸出日誌,載入圖片,緩衝等等模組裡面。另外不要為了1個或者2個功能而匯入整個library,如果沒有一個合適的庫與你的需求相吻合,你應該考慮自己去實現,而不是匯入一個大而全的解決方案。

6. Other

6.1 謹慎使用LargeHeap屬性

可以通過在manifest的application標籤下添加largeHeap=true的屬性來為應用聲明一個更大的heap空間(可以通過getLargeMemoryClass()來擷取到這個更大的heap size閾值)。然而,聲明得到更大Heap閾值的本意是為了一小部分會消耗大量RAM的應用(例如一個大圖片的編輯應用)。不要輕易的因為你需要使用更多的記憶體而去請求一個大的Heap Size。只有當你清楚的知道哪裡會使用大量的記憶體並且知道為什麼這些記憶體必須被保留時才去使用large heap,使用額外的記憶體空間會影響系統整體的使用者體驗,並且會使得每次gc的已耗用時間更長。在任務切換時,系統的效能會大打折扣。另外, large heap並不一定能夠擷取到更大的heap。在某些有嚴格限制的機器上,large heap的大小和通常的heap size是一樣的。

6.2 謹慎使用多進程

多進程確實是一種可以協助我們節省和管理記憶體的進階技巧。如果你要使用它的話一定要謹慎使用,因為絕大多數的應用程式都不應該在多個進程當中啟動並執行,一旦使用不當,它甚至會增加額外的記憶體而不是幫我們節省記憶體;同時需要知曉多進程帶來的缺點。這個技巧比較適用於那些需要在後台去完成一項獨立的任務,和前台的功能是可以完全區分開的情境。

這裡舉一個比較適合去使用多進程技巧的情境,比如說我們正在做一個音樂播放器軟體,其中播放音樂的功能應該是一個獨立的功能,它不需要和UI方面有任何關係,即使軟體已經關閉了也應該可以正常播放音樂。如果此時我們只使用一個進程,那麼即使使用者關閉了軟體,已經完全由Service來控制音樂播放了,系統仍然會將許多UI方面的記憶體進行保留。在這種情境下就非常適合使用兩個進程,一個用於UI展示,另一個則用於在後台持續地播放音樂。

6.3 實現方式可能存在的問題:例如啟動頁閃屏圖,show完畢之後應該釋放掉Bitmap。

一些實現方式看起來沒有問題實現了功能但是實際上可能對記憶體造成了影響。我在使用Heap Viewer查看Bitmap對象時發現了一張只需下載不應該被載入的圖。

使用HeapViewer可直接查看Bitmap

記憶體中出現的不應該被載入的圖

通過查閱代碼,發現問題出在:此處下載圖片作為另一個模組的使用圖,但是下載的方法竟然是使用圖片載入器載入出來Bitmap然後再儲存到本地;而且儲存之後也沒有將Bitmap對象釋放掉。

與之類似的還有:首頁閃屏圖展示之後,Bitmap對象應該及時釋放掉。

6.4 使用try catch進行捕獲

對高風險OOM代碼塊如展示高清大圖等進行try catch,在catch塊載入非高清的圖片並做相應記憶體回收的處理。注意OOM是OutOfMemoryError,不能使用Exception進行捕獲。

7. Summary

記憶體最佳化的套路:

(1)解決所有的記憶體流失

  • 整合LeakCanary,可以方便的定位出90%的記憶體流失問題;
  • 通過反覆進出可疑介面,觀察記憶體增減的情況,Dump Java Heap擷取當前堆棧資訊使用MAT進行分析。
  • 記憶體流失的常見情形可參照《Android 記憶體流失分析心得》

(2)避免記憶體抖動

  • 避免在迴圈中建立臨時對象;
  • 避免在onDraw中建立Paint、Bitmap對象等。

(3)Bitmap的使用

  • 使用三方庫載入圖片一般不會出記憶體問題,但是需要注意圖片使用完畢的釋放,而不是被動等待釋放。
  • 使用最佳化過的資料結構
  • 使用onTrimMemory根據不同的記憶體狀態做相應處理

(4)Library的使用

  • 去掉無用的Library,對產生的Apk進行反編譯查看使用到的Library,避免出現無用的Lib仍然被打進Apk;
  • 避免引入巨大的Library;
  • 使用Proguard進行混淆、壓縮。

========

 

 

========

安卓效能最佳化 視頻

相關文章

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在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.