內(nèi)存優(yōu)化(二) ---- Android APP性能優(yōu)化

由于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)不用)
image

軟引用實例:

    // 例子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)化中最重要的部分

    Android內(nèi)存泄露OOM的原因及解決方案

  • 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)測工具(四)

Android APP性能優(yōu)化之 ---- APK瘦身 App啟動優(yōu)化

Android內(nèi)存泄露OOM異常處理優(yōu)化

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市拱雏,隨后出現(xiàn)的幾起案子棉安,更是在濱河造成了極大的恐慌,老刑警劉巖铸抑,帶你破解...
    沈念sama閱讀 216,843評論 6 502
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件贡耽,死亡現(xiàn)場離奇詭異,居然都是意外死亡鹊汛,警方通過查閱死者的電腦和手機(jī)蒲赂,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,538評論 3 392
  • 文/潘曉璐 我一進(jìn)店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來刁憋,“玉大人滥嘴,你說我怎么就攤上這事≈脸埽” “怎么了若皱?”我有些...
    開封第一講書人閱讀 163,187評論 0 353
  • 文/不壞的土叔 我叫張陵镊叁,是天一觀的道長。 經(jīng)常有香客問我走触,道長晦譬,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 58,264評論 1 292
  • 正文 為了忘掉前任互广,我火速辦了婚禮敛腌,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘惫皱。我一直安慰自己像樊,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,289評論 6 390
  • 文/花漫 我一把揭開白布旅敷。 她就那樣靜靜地躺著生棍,像睡著了一般。 火紅的嫁衣襯著肌膚如雪扫皱。 梳的紋絲不亂的頭發(fā)上足绅,一...
    開封第一講書人閱讀 51,231評論 1 299
  • 那天捷绑,我揣著相機(jī)與錄音韩脑,去河邊找鬼。 笑死粹污,一個胖子當(dāng)著我的面吹牛段多,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播壮吩,決...
    沈念sama閱讀 40,116評論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼进苍,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了鸭叙?” 一聲冷哼從身側(cè)響起觉啊,我...
    開封第一講書人閱讀 38,945評論 0 275
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎沈贝,沒想到半個月后杠人,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 45,367評論 1 313
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡宋下,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,581評論 2 333
  • 正文 我和宋清朗相戀三年嗡善,在試婚紗的時候發(fā)現(xiàn)自己被綠了。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片学歧。...
    茶點(diǎn)故事閱讀 39,754評論 1 348
  • 序言:一個原本活蹦亂跳的男人離奇死亡罩引,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出枝笨,到底是詐尸還是另有隱情袁铐,我是刑警寧澤揭蜒,帶...
    沈念sama閱讀 35,458評論 5 344
  • 正文 年R本政府宣布,位于F島的核電站剔桨,受9級特大地震影響忌锯,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜领炫,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,068評論 3 327
  • 文/蒙蒙 一偶垮、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧帝洪,春花似錦似舵、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,692評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至砰奕,卻和暖如春蛛芥,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背军援。 一陣腳步聲響...
    開封第一講書人閱讀 32,842評論 1 269
  • 我被黑心中介騙來泰國打工仅淑, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人胸哥。 一個月前我還...
    沈念sama閱讀 47,797評論 2 369
  • 正文 我出身青樓涯竟,卻偏偏與公主長得像,于是被迫代替她去往敵國和親空厌。 傳聞我的和親對象是個殘疾皇子庐船,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 44,654評論 2 354

推薦閱讀更多精彩內(nèi)容