android垃圾回收機制
目錄
垃圾記憶體不及時回收,則執行時的可用記憶體會越來越少,最終導致OOM(記憶體溢位)。而垃圾回收(GC),主要可從兩個方面探討:
1.怎麼判定是"垃圾"?
2.怎麼回收的?
1."垃圾的判定"
垃圾的判定主要是針對堆記憶體中的物件、陣列等,當物件超出作用域,不被引用時,就可以被認定為是垃圾。
1)JDK1.1 前的判定規則-引用計數演算法
當物件建立時候,都被繫結一個計數器,當物件被引用則計數+1,引用失效或超出作用域則計數-1,計數為0時即被Jvm判定為"垃圾"。效率雖高,但是A和B物件如果同時互相引用,計數都為1,即使A、B不再被使用,Jvm也不會檢測到。而且,每次物件被引用時等操作還要觸發計數,因此,額外開銷在所難免,也就被棄用了。
2)目前在用的可達性分析演算法
從"GC Root“物件開始,向下搜尋,遇到可達的物件,就會形成一條鏈,將可達物件與"GC Root“關聯。最終不與任何"GC Root“鏈相關的物件,都會被判定為“垃圾”。如下物件可認定為"GC Root"物件:
1.虛擬機器棧(棧幀中的本地變量表)中引用的物件 2.方法區中靜態屬性引用的物件 3.方法區中常量引用的物件 4.本地方法棧中JNI引用的物件 5.活著的執行緒Thread
2.回收演算法
1)標記清除演算法
首先,會先標記所有需要回收的物件,接著將被標記的不可用的物件清除。優點是演算法簡單,只需要清理被標記的地址空間。缺點也比較明顯,因為標記位置零散,很碎片化,導致被清除完後,可用的地址空間也零零散散。如果連續的可用記憶體空間少,那麼遇到大的記憶體物件需分配記憶體時,就很難分配到適宜的連續記憶體空間。
2)複製演算法
將記憶體空間平分為A、B兩塊,將A中存活的物件移動到B記憶體塊中,再對A塊所有物件進行清除。但是這個演算法也有明顯的缺點,那就是不管A區域或B區域有多少個存活物件,都需要將整塊記憶體分成兩個區域,意味著能夠真正使用的記憶體變成了一半,造成記憶體的利用率不高。
3)標記整理演算法
類似標記清理演算法,將存活的記憶體物件標記,並平移到記憶體空間一側,然後清理記憶體空間另側資料。此演算法解決了標記清除演算法的導致的記憶體空間碎片化的問題。但是,缺點也有,就是多出了將存活記憶體平移的操作,會降低清除的效率。
4)分代回收演算法
將物件進行代的劃分,把不同生命週期的物件放在不同的代上使用不同的垃圾回收方式。主要代劃分如下:
年輕代(例如:方法的區域性變數等)
年老代(例如:快取物件、單例物件等)
持久代(例如:載入過的類資訊)
由上可知:堆大小=新生代+老年代
a、持久代主要存放的是類資訊,所以與java物件的回收關係不大,與回收相關的是年輕代和年老代。
b、年輕代又被分為3個部分:Enden區和兩個Survivor區(From和to:這兩個區是對稱的,沒先後關係)
記憶體大小比值為------>Enden區:From區:to區=8:1:1
新建立的物件會被放入Eden區,Eden區滿了會執行小GC,會把存活物件轉移(複製演算法)到From或者to區,假設為From區。接著,Eden區接著儲存新建立的物件,再次滿時,Eden區和From區會執行小GC,將存活物件轉移到to區。每次小GC後,存活物件都會在From或者to區,但是From或者to區也不會一直做苦力,多次轉移相同的存活物件。因此,From和to區會有一個閾值,如果轉移相同存活物件的次數超過閾值,這些物件就會被存放到老年代。
c、老年代默默存著年輕代轉移過來的資料,當空間被用完時,會觸發大GC(“標記-清除”或者“標記-整理”演算法)。由於回收時間比較長,因此需要避免頻繁觸發大GC。
3.記憶體優化
1)物件無需引用,則置空為null,方便檢測被垃圾記憶體
2)多用基本資料型別,少用它們的引用資料型別。例如Integer會比int所佔大
3)少用static修飾變數,被修飾的全域性性的變數是不會被GC回收的
4)字串拼接要用StringBuffer(用append拼接)替代String,例如String str =str1+str2+str3+str4+str5,每多一個“+”,就會多建立一個物件。
5)bitmap、遊標Cursor、IO或者檔案流等不用時候,記得回收。尤其是圖片的載入而佔用記憶體較大,可以做圖片質量或者物理大小的壓縮。
6)避免在頻繁繪製的ondraw方法中建立物件
7)Hashmap由於建立時候記憶體生成16位儲存Entry節點的陣列,一個Entry佔空間32B。也就是說即使裡面沒有任何元素,也要分別一塊記憶體空間給它。且儲存資料每次大於當前容量最大值,Hashmap都會以X2容量的方式去擴容。所以在Android中,HashMap是比較費記憶體的。可以嘗試android平臺自帶的SparseArray或者ArrayMap
詳解:https://blog.csdn.net/u010687392/article/details/47809295
8)防止單例類長久持有不用物件的引用,導致物件無法回收,特別是傳入上下文物件的單例類,可嘗試傳入ApplicationContext
9)非靜態內部類導致記憶體洩露,比如Activity中建立的Handler,可以嘗試弱引用去拿到外部的物件引用。
詳解:https://blog.csdn.net/qq_37321098/article/details/81535449
10)廣播的登出,頁面Activity退出時未執行完的Thread的登出或者Timer還在執行定時任務的登出等等。
11)Activity結束時候,需cancel掉屬性動畫。
12)webview退出的銷燬
mWebViewContainer.removeView(mWebView);
mWebView.stopLoading();
mWebView.getSettings().setJavaScriptEnabled(false);
mWebView.clearHistory();
mWebView.removeAllViews();
mWebView.destroy();
記得以前,webview如果內部播放視訊,有背景聲音,即使做了銷燬,聲音也還在,後來是頁面銷燬時,先reload過載webview去關閉聲音,再銷燬webview。有好方法,可以告訴下哦,謝謝!