由于Android應(yīng)用的沙箱機(jī)制,每個應(yīng)用所分配的內(nèi)存大小是有限度的,因此內(nèi)存會顯得非常珍貴,如果我們的內(nèi)存占用超過了一定的水平就會出現(xiàn)OutOfMemory錯誤
內(nèi)存概述
RAM(random access memory)隨機(jī)存取存儲器.(通俗的說就是內(nèi)存)
- Java的內(nèi)存分配策略:
Java內(nèi)存分配時會涉及到以下區(qū)域:
棧(Stack):一些基本類型的變量和對象的引用都是在棧內(nèi)存中分配,當(dāng)超過變量的作用域后,java會自動釋放該變量分配的內(nèi)存(對象本身不存放在棧中,而是存放在堆中)
堆(Heap): 通常用來存放new出來的對象和數(shù)組,由java垃圾回收器回收.
靜態(tài)存儲區(qū)(static field): 編譯時就分配好,在程序整個運(yùn)行期間都存在.它主要存放靜態(tài)數(shù)據(jù)和常量
還一個CPU存儲區(qū):
寄存器(Registers): 速度最快的存儲場所,因為寄存器位于處理器內(nèi)部,我們在程序中無法控制
- 堆棧的特點(diǎn):
棧:
定義一個變量時,Java在棧中為這個變量分配內(nèi)存空間,當(dāng)該變量退出該作用域后,Java會自動釋放為該變量所分配的內(nèi)存空間.
棧的存取速度比堆要快,僅次于寄存器.但是存在棧中的數(shù)據(jù)大小與生存期必須是確定的,缺乏靈活性
棧中的數(shù)據(jù)可以共享,它是由編譯器完成的,有利于節(jié)省空間
例如:需要定義兩個變量int a = 3匾效;int b = 3滨嘱;
編譯器先處理int a = 3;首先它會在棧中創(chuàng)建一個變量為a的引用,然后查找棧中是否有3這個值,如果沒有,就將3存放進(jìn)來再將a指向3.
接著處理int b = 3,創(chuàng)建完b的引用變量后在棧中已經(jīng)有3這個值,便將b直接指向3.這樣,就出現(xiàn)了a與b同時均指向3的情況.
這時,如果再讓a=4,那么編譯器會重新搜索棧中是否有4值,如果沒有,則將4存放進(jìn)來,并讓a指向4.
如果已經(jīng)有了,則直接將a指向這個地址.因此a值的改變不會影響到b的值那伐。
堆:
當(dāng)堆中通過new產(chǎn)生數(shù)組和對象超出其作用域后,它們不會被釋放,只有在沒有引用變量指向它們的時候才變成垃圾,不能再被使用,并且只有等被垃圾回收器回收才回釋放內(nèi)存.這也是Java比較占內(nèi)存的原因.
堆是一個運(yùn)行時數(shù)據(jù)區(qū),可以動態(tài)地分配內(nèi)存大小,因此存取速度較慢.
如上例子,棧中a的修改并不會影響到b,而在堆中一個對象引用變量修改了這個對象的內(nèi)部狀態(tài),會影響到另一個對象引用變量
- APP內(nèi)存占用信息查詢
float max = Runtime.getRuntime().maxMemory() * 1.0f / (1024 * 1024);
float total = Runtime.getRuntime().totalMemory() * 1.0f / (1024 * 1024);
float free = Runtime.getRuntime().freeMemory() * 1.0f / (1024 * 1024);
查看系統(tǒng)設(shè)置單個進(jìn)程的內(nèi)存上限
C:\Users\Administrator>adb shell
sagit:/ $ getprop|grep heapgrowthlimit
[dalvik.vm.heapgrowthlimit]: [256m]
- java中四種引用類型:
強(qiáng)引用(StrongReference):強(qiáng)引用是使用最普遍的引用(如:Object object=new Object(),object就是一個強(qiáng)引用了),
如果一個對象具有強(qiáng)引用內(nèi)存不足時,寧拋異常OOM導(dǎo)致程序終止也不回收,也就是JVM停止時才終止
軟引用(SoftReference):如果內(nèi)存空間不足時,才會被回收(當(dāng)內(nèi)存達(dá)到一個閥值,GC就會去回收它)
弱引用(WeakReference):不管當(dāng)前內(nèi)存空間是否足夠,在GC 時都會回收
虛引用(PhantomReference):顧名思義,就是形同虛設(shè),任何時候都可能被GC回收(已經(jīng)不用)
軟引用實例:
// 例子1:
private Map<String, SoftReference<Bitmap>> imageCache = new HashMap<String, SoftReference<Bitmap>>();
public void add(String path) {
Bitmap bitmap = BitmapFactory.decodeFile(path); // 這里的bitmap屬于強(qiáng)引用
SoftReference<Bitmap> softBitmap = new SoftReference<Bitmap>(bitmap); // 軟引用的Bitmap對象
imageCache.put(path, softBitmap);
}
public Bitmap get(String path) {
SoftReference<Bitmap> softBitmap = imageCache.get(path);
if (softBitmap == null) {
return null;
}
return softBitmap.get(); // 取出軟引用的Bitmap,如果內(nèi)存不足被回收,獲取為NUll
}
public static Bitmap readBitmap(Context context, int resId) {
BitmapFactory.Options opt = new BitmapFactory.Options();
opt.inPreferredConfig = Bitmap.Config.RGB_565;
opt.inPurgeable = true;
opt.inInputShareable = true;
InputStream is = context.getResources().openRawResource(resId);
return BitmapFactory.decodeStream(is, null, opt);
}
// 例子2:
static class MyHandler extends Handler {
private SoftReference<Activity> reference;
public MyHandler(Activity activity) {
// 持有 Activity 的軟引用
reference = new SoftReference<Activity>(activity);
}
@Override
public void handleMessage(Message msg) {
Activity activity = reference.get();
if (activity != null && !activity.isFinishing()) {
switch (msg.what) {
// 處理消息
}
}
}
}
- 垃圾回收機(jī)制:
垃圾回收是指清理在內(nèi)存中不再需要的數(shù)據(jù)對象用狱,以便大塊內(nèi)存可以重新分配給新的對
象彻亲。一般來說封孙,一旦某個對象在 App 中沒有一個活動的引用欢伏,就可以作為垃圾被回收了。
垃圾回收器會先從根部的對象開始(它知道這些對象是活動的并且正被進(jìn)程所使用)嚣州,并
且沿著每個引用去查找它們的關(guān)聯(lián)鲫售。如果一個對象不在這個有效引用的列表中,那么它肯
定不會再被使用该肴,就可以被回收了情竹。此時,分配給這個對象的內(nèi)存空間也可以回收了
內(nèi)存優(yōu)化
-
內(nèi)存泄漏
內(nèi)存泄漏是內(nèi)存優(yōu)化中最重要的部分
-
IntentService的使用
IntentService是一種特殊的Service,繼承自Service;用于在后臺執(zhí)行耗時的異步任務(wù),當(dāng)任務(wù)完成后會自動停止
為什么使用IntentService?
我們通常Service用法如下,也是標(biāo)準(zhǔn)用法:
public class MyService extends Service {
@Nullable
@Override
public IBinder onBind(Intent intent) {
return null;
}
@Override
public int onStartCommand(Intent intent, int flags, int startId) {
new Thread() {
@Override
public void run() {
// 處理耗時邏輯
stopSelf(); // 如需實現(xiàn)處理完自動停止功能,可這樣做
}
}.start();
return super.onStartCommand(intent, flags, startId);
}
}
如上寫法并沒有什么錯誤,但是需要寫如上額外代碼,同時 當(dāng)業(yè)務(wù)邏輯復(fù)雜后會有Service停止失敗導(dǎo)致內(nèi)存泄漏的風(fēng)險,Android官方推薦的最佳解決方案就是使用IntentService
public class MyIntentService extends IntentService {
public MyIntentService(String name) {
super(name);
}
@Override
protected void onHandleIntent(@Nullable Intent intent) {
// 處理耗時邏輯,處理完自動停止
}
}
內(nèi)部通過HandlerThread和Handler實現(xiàn)異步操作,創(chuàng)建IntentService時,只需實現(xiàn)onHandleIntent和構(gòu)造方法.onHandleIntent為異步方法,可執(zhí)行耗時操作.
-
Bitmap優(yōu)化
Bitmap是內(nèi)存消耗大戶,是導(dǎo)致OMM最常見的原因之一
圖片顯示:
我們可以根據(jù)場景需求去加載圖片的大小,例如列表中的小圖我們可以只加載縮略圖(thumbnails)
等比例壓縮圖片:
直接使用圖片(bitmap)會占用較多資源,特別是圖片較大的時候,可能導(dǎo)致崩潰,這時,我們可以使用BitmapFactory.Options設(shè)置inSampleSize.inSampleSize表示縮略圖大小為原始圖片大小的幾分之一,即如果這個值為2,則獲取圖片的寬和高都是原始圖片的1/2,圖片大小就為原始大小的1/4.
BitmapFactory.Options options = new BitmapFactory.Options();
// 該值設(shè)為true后將不返回實際的bitmap,也不給其分配內(nèi)存空間.但允許我們查詢圖片的信息,計算出原始圖片的長和寬
options.inJustDecodeBounds = true;
//縮放的倍數(shù),圖片寬高都為原來的二分之一匀哄,即圖片為原來的四分之一,SDK中建議該值為2,值越大會導(dǎo)致圖片不清晰
options.inSampleSize = 2;
options.inJustDecodeBounds = false;
Bitmap bmp = BitmapFactory.decodeFile(sourceBitmap, options);
圖片像素:
Android中圖片有四種屬性秦效,分別是:
ALPHA_8:每個像素占用1byte內(nèi)存
ARGB_4444:每個像素占用2byte內(nèi)存
ARGB_8888:每個像素占用4byte內(nèi)存 (默認(rèn))
RGB_565:每個像素占用2byte內(nèi)存
Android默認(rèn)的顏色模式為ARGB_8888,這個顏色模式色彩最細(xì)膩,顯示質(zhì)量最高.同時占用的內(nèi)存也最大 所以對圖片效果不是特別高的情況下可以使用RGB_565(565沒有透明度屬性)
public static Bitmap readBitmap(Context context, int resId) {
BitmapFactory.Options opt = new BitmapFactory.Options();
opt.inPreferredConfig = Bitmap.Config.RGB_565;
opt.inPurgeable = true;
opt.inInputShareable = true;
InputStream is = context.getResources().openRawResource(resId);
return BitmapFactory.decodeStream(is, null, opt);
}
圖片回收:
使用Bitmap過后及時的調(diào)用Bitmap.recycle()方法來釋放內(nèi)存,不要等Android系統(tǒng)來進(jìn)行釋放
if (bitmap != null && !bitmap.isRecycled()) {
// 回收并且置為null
bitmap.recycle();
bitmap = null;
}
System.gc();
對圖片采用軟引用
SoftReference<Bitmap> bitmap = new SoftReference<Bitmap>(pBitmap);
捕獲異常:
最壞的情況下不能導(dǎo)致程序崩潰,捕獲OOM異常
Bitmap bitmap = null;
try {
bitmap = BitmapFactory.decodeFile(path);
} catch (OutOfMemoryError e) {
e.printStackTrace();
}
if (bitmap == null) {
return defaultBitmap;
}
相關(guān)鏈接直達(dá):
Android APP性能優(yōu)化之 ---- 布局優(yōu)化(一)
Android APP性能優(yōu)化之 ---- 內(nèi)存優(yōu)化(二)
Android APP性能優(yōu)化之 ---- 代碼優(yōu)化(三)
Android APP性能優(yōu)化之 ---- 優(yōu)化監(jiān)測工具(四)