Android內存優化——概述

Android 應用運行在 Java 虛擬機上,每打開一個 Android app 都會打開一個獨立的虛擬機。運行虛擬機時會在設備的寄存器上開辟一塊內存空間作為虛擬機的內存區域。虛擬機的內存區域一般劃分為堆、棧、方法區/靜態存儲區、運行時常量池、本地方法棧等。

堆、棧

堆是不連續的內存區域(因為系統是用鏈表來存儲堆中的空閑內存地址)。堆上分配內存的過程為動態分配過程,在堆中存放由 new 關鍵字創建的對象或數組。堆的內存管理是由 Java 的 GC(垃圾回收機制)來管理的。通常我們所說的內存優化即是堆內存的優化。

棧是針對線程來說的,每個線程都有一個棧。棧中主要存放基本類型的變量對象的引用,并且對象本身是沒有存放在棧中,而是存放在堆中的。

當方法執行時,方法內部的局部變量被創建,基本類型的變量會直接存放在棧中,引用類型的變量則會將該引用變量在堆中的內存地址存放在棧中。方法執行結束時,這些局部變量在棧中所占的內存空間就會被釋放。

舉個例子:

public class Person {
    int i = 1;
    Girl g1 = new Girl();

    public void x() {
        int j = 1;
        Girl g2 = new Girl();
        Girl g3 = g1;
    }
}

ig1為成員變量,jg2g3為方法x()中的局部變量,ig1字段屬于類,類最終是要被new出來。所以ig1存放在堆中的。j為基本類型,作為局部變量肯定是存放在棧中的。g2g3都為引用類型,它們引用的對象實體是存放在堆中的,在棧中只存有他們的引用(堆中的內存地址),當方法執行結束后,棧中的引用被釋放。

總結: 成員變量(基本類型和引用類型)全部存儲在堆中;基本類型的局部變量存儲在棧中;引用類型的局部變量的對象實體是存在堆中,棧中只存有它的引用。

垃圾回收器 GC

棧內存的特點是效率高、速度快,并且在方法結束時就自動釋放,但是它的容量有限。而堆內存空間的釋放完全依賴于 GC 。在程序的運行過程中,GC 會不定時的被喚醒檢查是否有沒有被引用的對象,并釋放他們的空間。

一般在堆內存占用較多(內存不足)的空閑時候系統可能會自動執行垃圾回收。但是垃圾回收機制只針對“垃圾”有效。所謂垃圾,是指存在于堆中,并且程序不能再訪問到的對象(不可達的對象)。GC 在執行垃圾回收時,會將不可達的對象放入垃圾回收器中,而對于那些還處于引用狀態即可達對象 GC 會根據該對象的引用狀態來決定是否回收。

Java中對象的引用狀態有強引用、軟引用、弱引用、虛引用4種。

強引用

強引用是使用最普遍的引用方式,我們平常使用的大部分引用都是強引用。如果一個對象具有強引用,那垃圾回收器絕不會回收它。當內存空間不足,Java 虛擬機寧愿拋出 OutOfMemoryError 錯誤,使程序異常終止,也不會靠回收具有強引用的對象來解決內存不足的問題。

軟引用

一個對象具有軟引用,如果內存空間足夠,垃圾回收器就不會回收它;如果內存空間不足了,就會回收這些對象的內存。只要垃圾回收器沒有回收它,該對象就可以被程序使用。

public class Test {
    public static void main(String[] args) {
        Person person = new Person();
        SoftReference<Person> sr = new SoftReference<Person>(person);
        person = null;
        if (sr != null) {
            person = sr.get();
        } else {
            person = new Person();
            sr = new SoftReference<Person>(person);
        }
    }
}
弱引用

在垃圾回收器線程掃描它所管轄的內存區域的過程中,一旦發現了具有弱引用的對象,不管當前內存空間足夠與否,都會回收它的內存。

public class Test {
    public static void main(String[] args) {
        Person person = new Person();
        WeakReference<Person> wr = new WeakReference<Person>(person);
        person = null;
        if (wr != null) {
            person = wr.get();
        } else {
            person = new Person();
            wr = new WeakReference<Person>(person);
        }
    }
}
虛引用

如果一個對象僅持有虛引用,那么它就和沒有任何引用一樣,在任何時候都可能被垃圾回收器回收。虛引用主要用來跟蹤對象被垃圾回收的活動,我們平常一般不會使用。

需要注意的是,GC 什么時候回收垃圾是無法控制的,垃圾回收時間也是無法預料的。

GC 內存管理機制雖然能幫助我們自動管理內存,避免了程序員主動釋放內存的繁瑣工作,但它不能解決所有的問題。GC不能回收的對象可能會導致內存泄露,甚至使整個程序發生OOM。

內存泄露

當一個對象在程序執行過后已經不需要再使用了,但是有其他的對象仍然持有該對象的引用,以致該對象不能被 GC 回收,那么這個對象會一直占據內存,從而導致該內存不可用。這種本該被GC回收(不再需要用了)而又不能被回收(被其他對象持有引用)以致停留在堆內存中的對象就造成了內存泄露。

內存溢出

內存溢出,OutOfMemory ,即 我們通常所說的OOM,是指程序在申請內存時,沒有足夠的內存空間供其使用。

在Android中,有以下幾種情況可能發生內存溢出:
1.內存泄露可能導致內存溢出的發生;
2.一次加載的數據量過大,如一次從數據庫讀取過多數據;
3.保存了許多耗用內存過大的對象(如Bitmap)或加載單個超大的圖片,造成內存超出限制;
4.代碼中存在死循環或循環產生過多重復的對象實體。

GC能自動回收并釋放無用的對象,但是如果程序頻繁的GC也會導致一些問題。

頻繁GC

程序在執行GC操作的時候,任何線程的任何操作都會需要暫停,等待GC操作完成之后,其他操作才能夠繼續運行。故而如果Andorid應用頻繁GC,UI線程有可能會暫停,從而導致導致界面卡頓。

導致頻繁GC主要有兩個方面的原因:
1.內存抖動, 即大量的對象被創建又在短時間內馬上被釋放。
2.瞬間產生大量對象會嚴重占用內存區域,當達到閥值, 剩余空間不夠的時候,就會觸發GC。即使每次分配的對象需要占用很少的內存,但是他們疊加在一起會增加堆內存的壓力, 從而觸發更多的GC。


OK,Android內存優化的概述就到此為止。后面會詳細介紹Android中的內存泄露及相關優化。

最后編輯于
?著作權歸作者所有,轉載或內容合作請聯系作者
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發布,文章內容僅代表作者本人觀點,簡書系信息發布平臺,僅提供信息存儲服務。

推薦閱讀更多精彩內容