Android的內存泄漏

參考鏈接

java的內存

棧區、堆區、靜態區/方法區

1、棧區:由編譯知器自動分配釋放,存放函數的參數值、局部變量的值等、基本類型的變量,例如int a=3中的a、對象的引用變量,例如Thread t=new Thread()中的t、具體方法執行結束之後,系統自動釋放JVM內存資源。

2、堆區:一般由程序員分配釋放,存放由new創建的對象和數組,jvm不定時道查看這個對象,如果沒有引用指向這個對象就內回收。

3、靜態區/方法區:存放全局變量,靜態變量和字符串常量,不釋放和整個應用的生命週期一樣。

堆內存和棧內存,兩者的區別?

  1. 引用變量是普通變量,定義時在棧內存中分配,引用變量在程序運行到作用域外後被釋放。而數組和對象本身在堆中分配,即使程序運行到使用new產生數組和對象的語句所在的代碼塊之外,數組和對象本身佔用的堆內存也不會被釋放。數組和對象在沒有引用變量指向它的時候,才變成垃圾,不能再被使用,但是仍然佔着內存,在隨後的一個不確定的時間被垃圾回收器釋放掉,這個也是Java比較佔內存的主要原因。實際上,棧中的引用變量指向堆內存中的變量,這就是Java中的指針。

  2. 通俗來講,堆是用來存放對象的,而棧是用來執行程序的。

  3. jvm只有一個堆區(heap),被所有線程共享;
    每個線程包含一個棧區(stack),每個棧中的數據都是私有的,其他的棧不能訪問,但是同一個棧中變量是共享的;分爲3個部分:基本類型變量區,執行環境上下文,操作指令區。

爲什麼要有堆和棧?這樣設計有什麼好處?

  1. Java自動管理堆和棧,程序員不能直接地設置棧和堆。
  2. Java的堆是一個運行時數據區。堆是由JVM的垃圾回收器自動管理的。堆的優勢是可以在程序運行時,動態地分配內存
    大小,但是正是由於這個原因,它的存取速度較慢。
  3. 棧的優勢是,存取速度比堆要快,僅次於寄存器,棧數據可以共享。但缺點是,存在棧中的數據大小和生存期是必須確定的,缺乏靈活性。
    棧有一個很重要的特性,就是存在棧中的數據可以共享。假設我們同時定義:

int a = 3;

int b = 3;

編譯器先處理int a = 3;首先它會在棧中創建一個變量爲a的引用,然後查找棧中是否有3這個值,如果沒找到,就將3存放進來,然後將a指向3。接着處理int b = 3;在創建完b的引用變量後,因爲在棧中已經有3這個值,便將b直接指向3。這樣,就出現了a與b同時均指向3的情況。這時,如果再令a=4;那麼編譯器會重新搜索棧中是否有4值,如果沒有,則將4存放進來,並令a指向4;如果已經有了,則直接將a指向這個地址。因此a值的改變不會影響到b的值。要注意這種數據的共享與兩個對象的引用時
指向一個對象的這種共享是不同的,因爲這種情況a的修改並不會影響到b, 它是由編譯器完成的,它有利於節省空間。而一個對象引用變量修改了這個對象的內部狀態,會影響到另一個對象引用變量。

內存泄漏分析:

強引用

Object obj = new Object(); //只要obj還指向Object對象,Object對象就不會被回收
obj = null;  //手動置null,幫助垃圾收集器回收此對象

單例的內存泄漏

單例的內存泄露 當Android想要釋放Activtity的時候 有單例(單例是static類型的 他的生命週期和整個程序一樣)使用了Activity的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;
   }
}

內部類

非靜態內部類持有外部類實例的強引用。

如果我們在內部類創建一個靜態的實例,該實例的生命週期和程序一樣長,導致了該實例一直持有外部類的引用 導致該外部類的內存一直無法釋放。

class test{
  private static final TAG = "";
}

解決方法:
1.將內部類更改爲靜態內部類

static class test{
  private static final TAG = "";
}

2.避免靜態變量

匿名內部類

如果這個非靜態內部類實例內部執行耗時任務期間,Activity不幸被銷燬了,就會導致外圍對象不會被回收,從而導致內存泄漏

AsyncTask

void startAsyncTask() {
    new AsyncTask<Void, Void, Void>() {
        @Override protected Void doInBackground(Void... params) {
            while(true);
        }
    }.execute();
}

解決方法

private static class NimbleTask extends AsyncTask<Void, Void, Void> {
    @Override protected Void doInBackground(Void... params) {
        while(true);
    }
}

void startAsyncTask() {
    new NimbleTask().execute();
}

Handler

// 容易造成內存泄漏的寫法:
   private Handler mHandler = new Handler() {
       @Override
       public void handleMessage(Message msg) {
           //...刷新頁面
       }
   };

   @Override
   protected void onCreate(Bundle savedInstanceState) {
       super.onCreate(savedInstanceState);
       setContentView(R.layout.activity_main);
       loadData();
   }

   private void loadData() {
       //...request
       Message message = Message.obtain();
       mHandler.sendMessage(message);
   }

解決方法

//    修復內存泄漏的方法:
    private MyHandler mHandler = new MyHandler(this);
    private TextView mTextView ;
    private static class MyHandler extends Handler {
        private WeakReference<Context> reference;
        public MyHandler(Context context) {
            reference = new WeakReference<>(context);
        }
        @Override
        public void handleMessage(Message msg) {
            MainActivity activity = (MainActivity) reference.get();
            if(activity != null){
//                activity.mTextView.setText("");
            }
        }
    }

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        loadData();
    }

    private void loadData() {
        //...request
        Message message = Message.obtain();
        mHandler.sendMessage(message);
    }

    @Override
    protected void onDestroy() {
        super.onDestroy();
        mHandler.removeCallbacksAndMessages(null);
    }

Thread

void testThread() {
   new Thread(new Runnable() {
           @Override
           public void run() {
               while (true) {
                   try {
                       Thread.sleep(1000);
                   } catch (InterruptedException e) {
                       e.printStackTrace();
                   }
               }
           }
       }).start();
}

解決方法

private Thread thread;

@Override
public void onDestroy() {
    super.onDestroy();
    if (thread != null) {
        thread.interrupt();
    }
}

void spawnThread() {
    thread = new Thread(new Runnable() {
           @Override
           public void run() {
               while (!isInterrupted()) {
                   try {
                       Thread.sleep(1000);
                   } catch (InterruptedException e) {
                       e.printStackTrace();
                   }
               }
           }
       }).start();
}

bitmap資源未釋放

建議手動調用recycle()方法,釋放其Native內存:

if(bitmap != null && !bitmap.isRecycled()){  
    bitmap.recycle(); 
    bitmap = null; 
}
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章