虛擬機
jvm
1. 加載程序:
1,A.java經過編譯器編譯,生成A.class位元組碼檔案,多個class檔案會被打包成jar檔案
2,程式訪問A這個類時,會通過ClassLoader類加載器將A.class加載到jvm的記憶體中
dalvik+JIT
1. 加載程序:
1,A.java經過編譯器編譯,生成A.class位元組碼檔案
2,dx工具將class檔案打包成dex檔案
2. JIT(Just In Time Compiler):
即時編譯策略,即動態編譯
缺點:
1,每次啟動應用都要重新編譯(沒有快取)
2,所以運行時比較耗電
art+AOT
1. AOT(Ahead Of Time)
提前編譯策略,即靜態編譯
缺點:
1,應用首次安裝和系統升級之后(需要重新編譯)比較耗時
2,編譯后的檔案占用更多的記憶體空間,快取的結果
2. JIT和AOT的區別
無非就是空間換時間
記憶體
dalvik記憶體(即java記憶體)
heap(堆記憶體):執行緒共享區域
1. 存盤資料型別
1,成員變數
基本資料型別:其變數名及其資料值存放在堆記憶體中
-
參考資料型別:物件存放在堆記憶體中,其變數名和地址值存放在堆疊中,該地址值指向所參考的物件
2,區域變數
參考資料型別:物件存放在堆記憶體中,其變數名和地址值存放在堆疊中,該地址值指向所參考的物件
2. 記憶體釋放
GC
stack(堆疊記憶體):執行緒私有區域
1. 存盤資料型別
1,區域變數
基本資料型別:其變數名及其資料值存放在堆疊記憶體中
疑問:區域變數中的基本資料型別都是存盤在堆疊記憶體中嗎?
不是,
int[] array=new int[]{1,2};
由于new了一個物件,所以new int[]{1,2}這個物件是存盤在堆中的,也就是說1,2這兩個基本資料型別是存盤在堆中
2. 記憶體釋放
方法結束,記憶體自動釋放,有自己的作用域
方法區:執行緒共享區域
1. 存盤資料型別
方法區中包含的都是在整個程式中永遠唯一的元素,如class,static變數
特殊型別String
String物件創建有兩種方式:
1. 字面量形式:物件的參考存盤在字串常量池(在方法區中)
String str1 = "aaa";
解釋:字串常量池中不存在aaa這個字串物件的參考,所以新創建一個字串物件,然后將這個參考放入字串常量池,
String str2 = "aaa";
解釋:字串常量池中已經存在aaa這個字串物件的參考,于是將已經存在的字串物件的參考回傳給變數str2,這里不會再重新創建字串物件,
結論:System.out.println(str1 == str2); 結果為true,
2. 使用new創建
不論字串常量池有沒有相同內容物件的參考,都重新創建字串物件,然后將這個參考存盤到堆疊記憶體中,
String str3 = new String("aaa");
String str4 = new String("aaa");
結論:System.out.println(str3 == str4); 結果為false,
如果想讓new出來的String物件的參考加入到字串常量池中,可以使用intern方法,
即String str4 = str3.intern();這時候System.out.println(str3 == str4); 結果為true,
native記憶體
Android的java程式為什么容易出現OOM?
首先要知道一個概念:RAM,即物理記憶體,
因為Android系統對dalvik的vm heapsize(虛擬記憶體)做了硬性限制,根據定制系統不一樣,有16M、24M等,也就是說,在RAM充足的情況下,也可能發生OOM,
如何解決OOM
安卓代碼程式優化(后面著重講)
創建子行程
創建一個新的行程,就可以把一些物件分配到新行程的heap上,
方法:使用android:process標簽
缺點:會增加系統開銷
使用jni在native heap上申請空間
native heap的增長不受dalvik的vm heapsize的限制,
只要RAM有足夠的剩余空間,都可以在nativr heap上申請空間,
當然,如果RAM快耗盡了,memory killer會殺行程釋放RAM
---------------------------------------------------------------------
(所以這里會涉及到行程的優先級,后面著重講)
---------------------------------------------------------------------
修改vm heapsize的大小
Bitmap分配在dalvik heap還是native heap?
過多的創建bitmap會導致OOM,且native heap不受dalvik限制,所以bitmap只能是分配在dalvik heap上,因為只有這樣才能解釋bitmap容易導致OOM,
但是,有人可能會說,Bitmap確實是使用java native方法創建的啊,為什么會分配到dalvik heap中呢?
1,bitmap創建:
Bitmap bm = BitmapFactory.decodeResourceStream(res, value, is, pad, opts);


2.進入frameworks原始碼:
發現會呼叫到BitmapFactory.cpp中的deDecode方法,最侄訓呼叫到Graphics.cpp的createBitmap方法,

通過NewObject,創建Bitmap又回到了JAVA層,
Android記憶體優化(即安卓代碼程式優化)
解決記憶體溢位(OOM)
什么是記憶體泄漏,什么是記憶體溢位
1,記憶體泄漏:memory leak
程式申請記憶體后,被某個其他物件一直持有,無法釋放已申請的記憶體空間,(記憶體只進不出)
2,記憶體溢位:out of memory
當記憶體一直被申請,卻得不到釋放,記憶體空間太小,當程式在申請記憶體時,沒有足夠的空間供其使用,就出現OOM,
注意:記憶體泄漏/溢位不能try catch處理,
如何解決
1,大圖片和多圖片的優化
圖片如果太大,加載到記憶體時可能導致OOM,
解決:
1,合理使用JPG和PNG
JPG記憶體小,但解碼復雜
PNG記憶體大,但解碼簡單,如果PNG圖片過多,會造成頻繁GC,甚至OOM,
2,圖片壓縮:
2.1,createScaledBitmap();現成的API,但是這個API的使用前提條件是圖片需要先加載到記憶體中,
2.2,還有一個經常使用到的技巧是inJustDecodeBounds,可以事先獲取到圖片的大小,需要設定bitmapOptions.inJustDecodeBounds = true;
2.3,降低解析度
2.4,指定解碼方式:Bitmap.Config ARGB_8888 默認,一個像素點占32位,最占記憶體的
Bitmap.Config ARGB_4444 一個像素點占16位
3,bitmap物件不使用時,要recycle()釋放記憶體
4,盡量使用RecyclerView或者ListView替換ScrollView
2,聊一下圖片的三級快取
目的:減少不必要的流量消耗,增加加載速度
原理:
一級快取-->記憶體,LruCache,采用最近最少原則,把最近使用的通過LinkedhashMap來強參考持有,把最少使用的物件在快取值達到預設值之前就從記憶體中移除
二級快取-->本地,DiskLruCache,
三級快取-->網路
流程:先從記憶體加載,記憶體中有,就直接加載;記憶體中沒有,就從本地加載,若本地中有,加載的同時快取到記憶體;若本地也沒有,從網路加載,同時快取到記憶體和本地,
Glide自帶三級快取
skipMemoryCache(false),默認false,自動開啟記憶體快取,
diskCacheStrategy(DiskCacheStrategy.RESULT),開啟硬碟快取
DiskCacheStrategy.NONE: 表示不快取任何內容
DiskCacheStrategy.SOURCE: 表示只快取原始圖片
DiskCacheStrategy.RESULT: 表示只快取轉換過后的圖片(默認選項)
DiskCacheStrategy.ALL : 表示既快取原始圖片,也快取轉換過后的圖片
3,聊一下圖片加載框架
Glide:
可以處理大型的圖片流,圖片自動縮放的
Picasso:
體積非常小,圖片未縮放的
Fresco:
體積非常大,圖片存盤在系統的匿名共享記憶體,而不是dalvik heap記憶體中,所以不會導致OOM,減少了頻繁GC,性能更高,
4,強參考、軟參考、弱參考、虛參考
1,強參考:直接new出來的物件
String str = new String("aaa");
特點:物件任何時候都不會對系統回收,JVM寧愿拋出OOM例外,也不會回收強參考所指向的物件,
2,軟參考:SoftReference
String str = new String("aaa");
SoftReference s = new SoftReference(str);
if(s.get() != null){ // 一定要判空
String softStr = s.get();
}
特點:記憶體空間足,不回收;記憶體空間不足,就會回收
3,弱參考:WeakReference
String str = new String("aaa");
WeakReferences = new WeakReference(str);
if(s.get() != null){ // 一定要判空
String weakStr = s.get();
}
特點:無論記憶體空間是否足夠,只要發現,就會回收
4,虛參考:PhantomReference
特點:可以在任何時候被回收,無法通過get()方法來獲取物件實體,僅僅只能在物件被回收時收到一個通知,
我們知道,java的Object類里有個finalize()方法,它的作業原理是:一旦該物件即將被回收,會呼叫其finalize()方法,并且在下一次真正回收動作發生時,才會真正的回收它,
但是問題在于,GC時間是隨機的,finalize()方法被呼叫時間也是隨機的,使用虛參考就可以解決這個問題,虛參考可以精細的跟蹤記憶體使用情況,
到底什么時候用軟參考,什么時候用弱參考?
1,如果只是想避免OOM,則可以使用軟參考;如果對應用的性能更在意,則可以使用弱參考,
2,根據物件是否經常被使用,如果物件經常被使用,則盡量用軟參考,否則使用弱參考,
5,解決記憶體泄漏
5.1 單例導致的
單例的靜態特性使其生命周期和應用一樣長,所以傳入的context的生命周期至關重要,一定要傳入Application的Context,而不是Activity的Context,
5.2 非靜態類創建靜態實體
該靜態實體生命周期和應用一樣長,導致了該靜態實體一直持有該Activity的參考,該Activity銷毀后記憶體資源不能正常回收,
要么去掉static,要么將this換成Application的Context

5.3 非靜態內部類創建靜態實體
非靜態內部類會默認持有外部類的參考,而又使用了該非靜態內部類創建了一個靜態的實體,該實體的生命周期和應用的一樣長,這就導致了該靜態實體一直會持有該Activity的參考,導致Activity的記憶體資源不能正常回收,
要么將該內部類設為靜態內部類,因為靜態的內部類不會持有外部類的一個隱式參考;要么將該內部類抽取出來,如果需要使用Context,需要使用ApplicationContext ,

5.4 異步任務導致的
若Activity已經銷毀,但是異步任務還沒結束,需要將AsyncTask、Thread等設為靜態內部類,而且需要在Activity銷毀時取消相應的異步任務,
5.5 Handler導致的
Handler是非靜態匿名內部類,持有Activity的參考,雖然Handler實體不是靜態的,但是訊息佇列是在Looper執行緒中不斷輪詢處理訊息的,當Activity銷毀,訊息佇列中還有未處理的訊息,訊息佇列中的Message持有Handler實體的參考,Handler又持有Activity的參考,就會導致Activity記憶體資源無法正常回收,
將Handler設為靜態內部類,傳入的Context可以使用ApplicationContext,也可以將Context弱參考,而且需要在Activity銷毀時移除訊息佇列中的訊息,handler.removeCallbacksAndMessages,
5.6 影片導致的
屬性影片中可以設定無限回圈影片,該View持有該Activity物件的參考
需要在Activity銷毀時停止影片,objectAnimator.cancel(),
5.7 資源未關閉導致的
IO流未關閉、BroadcastReceiver未注銷、Bitmap未釋放等,
5.8 第三方庫導致的
EventBus,RxJava等,在Activity銷毀時需要解除訂閱
解決ANR
Android啟動優化
轉載請註明出處,本文鏈接:https://www.uj5u.com/yidong/274086.html
標籤:其他
