Android內存優化

首先咱們來解釋下兩個名詞:java

內存泄漏

當一個對象在程序執行事後已經不須要再使用了,可是有其餘的對象還持有該對象的引用,以至該對象不能被GC回收,那麼這個對象會一直佔用內存,從而致使該內存不可用,這種本該被GC回收(再也不須要用了)而又不能被回收(被其餘對象持有引用),以至停留在堆內存中的對象就形成了內存泄露.數組

內存溢出

內存溢出(OutOfMeory),即咱們一般所說的OOM,是指程序在申請內存時,沒有足夠的內存空間共其使用.緩存

內存泄露的危害

  • 過多的內存泄露最終會致使內存溢出(OOM)
  • 內存泄露致使內存不足,會觸發頻繁GC,從而致使UI卡頓

常見內存泄露問題及解決方案

1.單例

  • 主要緣由是由於通常狀況下單列都是全局的,有時候會引用一些實際生命週期比較短的變量,致使其沒法釋放.
  • 解決方案:
// 使用了單例模式 
// 若是Context使用的是Activity的Context,則會形成內存溢出
//單例的Context 使用Application的Context,單例的生命週期和應用的同樣長,這樣基本能夠防止單例引發來的內存泄露內存泄漏。
public class AppManager { 
    private static AppManager instance;  
    private Context context; private AppManager(Context context) { 
        this.context = context; 
     }  
    public static AppManager getInstance(Context context) {  
     if (instance != null) {  
         instance = new AppManager(context); 
      }  
      return instance;  
    }  
}
複製代碼

2.非靜態內部類建立靜態實例形成的內存泄漏

public class MainActivity extends AppCompatActivity { 
    private static TestResource mResource = null;  

    @Override protected void onCreate(Bundle savedInstanceState) { 
        super.onCreate(savedInstanceState); 
        setContentView(R.layout.activity_main);  
        if(mResource == null){  
            mResource = new TestResource(); 
         }  
         //...  
         }  
class TestResource { //... } }
複製代碼
  • 非靜態內部類默認會持有外部類的引用,而改非靜態內部類有建立了一個靜態實例,該實例的生命週期和應用的同樣長,這就致使了該靜態實例一直會持有該Actvity的引用,從而致使Activity的內存資源不能被正常的回收.
  • 解決方案:將改內部類設爲靜態內部類或者將該內部類抽取出來封裝成一個單列,若是須要使用Context,就使用Application的Context.

3.Handler形成的內存泄露

public class MainActivity extends AppCompatActivity {  
        private final Handler handler = new Handler() {   
        @Override public void handleMessage(Message msg) {  
        //  
        ... } 
        };  
         @Override protected void onCreate(Bundle savedInstanceState) {  
               super.onCreate(savedInstanceState);  
               setContentView(R.layout.activity_main); 
                new Thread(new Runnable() {  
                @Override public void run() { 
                 // ...  
                 handler.sendEmptyMessage(0x123);  
              } 
        });  
}
複製代碼
  • 緣由:當MainActivity結束時,未處理的消息持有Handler的引用,而handler又持有他所屬的外部類也就是MainActivity,這條引用關係會一直保持知道消息獲得處理,這樣阻止了MainActivity內垃圾回收器回收,從而形成內存泄漏
  • 解決方案:將Handler類獨立出來或者使用靜態內部類,這樣即可以免內存泄露

4.資源未關閉

  • 好比在Activity中register了一個BroadcastReceiver,可是Activity結束後沒有unregister該BroadcastReceiver
  • 資源性對象好比Cursor,Stream,File文件等每每都用了一些緩衝,咱們在不使用的時候,應該及時關閉它們,以便它們的緩衝及時關閉內存,它們的緩衝不只存在於java虛擬機內,還存在於java虛擬機外,若是咱們僅僅是把它的引用設置爲null,而不關閉它們,每每會形成內存泄露.
  • 對於資源性對象在不使用的時候,應該調用他的close()函數將其關閉,而後再設置爲null,在咱們的程序退出時,必定要確保咱們的資源性對象已經關閉
  • Bitmap對象再也不使用的時候調用recycle(),2.3之後的bitmap應該再也不須要手動recycle()了,內存已經在java層了

5.使用輕量的數據結構

  • 使用ArrayMap/SparseArray來代替HashMap,ArrayMap/SparseArray是專門爲移動設備設計的高效的數據結構

HashMap

  • HashMap內部使用了一個默認容量爲16的數組來存儲數據,採用拉鍊法解決hash衝突(數據+鏈表).
  • HashMap就算沒有數據,也須要分配默認16個元素的數組,一旦數據量達到HashMap限定容量的75%,就將按兩倍擴容

SparseArray

  • 支持int類型,避免自動裝箱,可是也只支持int類型的key
  • 內部經過兩個數據來進行數據存儲,一個存儲key,另一個存儲value
  • 由於key是int,在查找時,採用二分查找,效率高,SparseArray存儲的元素都是按元素的key值從小到大排列好的
  • 默認初四size爲0,每次增長元素,size++

ArrayMap

  • 跟SparseArray同樣,內部兩個數組,可是第一個存key的hash值,一個存value,對象按照key的hash值排序,二分查找也是按照hash
  • 查找index時,傳入key,計算出hash,經過二分查找hash數組,肯定index

6.用StringDef和IntDef來代替枚舉(Enum)

  • 枚舉佔用的內存過大,google官方建議用註解StringDef和IntDef來替換枚舉

7.Bitmap處理

  • 對Bitmap壓縮
  • Lru機制處理Bitmap
  • 使用有名的圖片緩存框架(我通常使用這種)

8.不要使用String進行字符串拼接

  • 嚴格的講,String拼接只能歸結到內存抖動中,由於產生的String副本可以被GC,不會形成內存泄漏
  • 頻繁的字符串拼接,使用StringBuffer或者StringBuiler代替String,能夠在必定程度上避免OOM和內存抖動

9.謹慎使用static對象

  • static對象的生命週期過程,應該謹慎使用
相關文章
相關標籤/搜索