Android異常優化之內存泄露實例與及部分解決方案

1.Java內存泄漏基礎知識

  內存泄漏的理解其實很簡單,就是該被釋放的對象沒有被釋放,一直被某個或者某些實例引用所持有,導致不能被垃圾回收。

1)Java內存的分配策略:

  a.靜態存儲區(方法區):跟堆一樣,被所有的線程共享。方法區包含所有的class和static變量,方法區中包含的都是在整個程序中永遠唯一的元素,如class,static變量。

  b.棧區:每個線程包含一個棧區,棧中只保存基礎數據類型的對象和自定義對象的引用(不是對象),對象都存放在堆區中,每個棧中的數據(原始類型和對象引用)都是私有的,其他棧不能訪問,棧分爲3個部分:基本類型變量區、執行環境上下文、操作指令區(存放操作指令)。

  c.堆區:動態內存分配,new出來的對象都存放在此區域,沒用的對象都會被GC進行回收。存儲的全部是對象,每個對象都包含一個與之對應的class的信息。(class的目的是得到操作指令) ,jvm只有一個堆區(heap)被所有線程共享,堆中不存放基本類型和對象引用,只存放對象本身
 

2、Java當中的內存泄露

       在Java中,內存泄漏就是存在一些被分配的對象,這些對象有下面兩個特點,首先,這些對象是可達的,即在有向圖中,存在通路可以與其相連(也就是說仍存在該內存對象的引用);其次,這些對象是無用的,即程序以後不會再使用這些對象。如果對象滿足這兩個條件,這些對象就可以判定爲Java中的內存泄漏,這些對象不會被GC所回收,然而它卻佔用內存。簡單來說就是無用對象(不再使用的對象)持續佔有內存或者無用對象得不到及時的釋放,從而造成內存空間的浪費叫內存泄漏。
 

3、Android中的內存泄露

    1.單例造成的內存泄漏

public class SingletonActivityContext {
    private static SingletonActivityContext instance;
    private Context context;

    private SingletonActivityContext(Context context) {
        this.context = context;
    }

    public static SingletonActivityContext getInstance(Context context) {
        if (instance == null) {
            instance = new SingletonActivityContext(context);
        }
        return instance;
    }
}
正確寫法應該是:
public class SingletonActivityContext {
    private static SingletonActivityContext instance;
    private Context context;

    private SingletonActivityContext(Context context) {
        this.context = context.getApplicationContext(); //使用Application的context
    }

    public static SingletonActivityContext getInstance(Context context) {
        if (instance == null) {
            instance = new SingletonActivityContext(context);
        }
        return instance;
    }
}
2.非靜態內部類創建靜態實例造成的內存泄漏
public class StaticLeakActivity extends Activity {
    private static noneStaticClass mResource = null;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        if (mResource == null) {
            mResource = new noneStaticClass();
        }
    }

    private class noneStaticClass {

    }
}
解決:
public class StaticLeakActivity extends Activity {
    private static noneStaticClass mResource = null;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        if (mResource == null) {
            mResource = new noneStaticClass();
        }
    }

     //修改爲靜態內部類,這樣就不會持有外部類的引用了
    private static class noneStaticClass {

    }
}
3.handler造成內存泄漏
public class HandlerLeakActivity extends Activity {
    private final Handler mLeakyHandler = new Handler() {
        @Override
        public void handleMessage(Message msg) {

        }
    };

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);

        mLeakyHandler.postDelayed(new Runnable() {
            @Override
            public void run() {
            }
        }, 1000 * 60 * 10);

        finish();
    }
    /*
    * 解決辦法:
    * 1.將 Handler 聲明爲靜態的
    * 2.通過弱引用的方式引入 Activity
    * 3.並在Acitivity的onDestroy()中調用handler.removeCallbacksAndMessages(null)及時移除所有消息。
    *
    * */
}
解決示例:
public class HandlerLeakActivity extends Activity {

   private MyHandler mleakHandler = new MyHandler(this)
    //1、修改爲靜態內部類    
    private static class MyHandler extends Handler {
        //2.通過弱引用的方式引入 Activity
        private WeakReference<Context> reference;
        
        public MyHandler(Context context){
            reference = new WeakReference<>(context);
        }

        @Override
        public void handleMessage(Message msg) {
            HandlerLeakActivity activity = (HandlerLeakActivity)reference.get();
            if(activity!=null){
                //todo業務邏輯
            }
        }
    };

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);

        mLeakyHandler.postDelayed(new Runnable() {
            @Override
            public void run() {
            }
        }, 1000 * 60 * 10);

        finish();
    }

    @Override
    public void onDestroy() {
       mLeakyHandler.removeCallbacksAndMessages(null);

    }
}
4.線程造成的內存泄漏
public class ThreadLeakActivity extends Activity {
    @Override
    protected void onCreate(@Nullable Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        testThreadLeak();
    }

    private void testThreadLeak() {
        new AsyncTask<Void, Void, Void>() {
            @Override
            protected Void doInBackground(Void... params) {
                SystemClock.sleep(10000);
                return null;
            }
        }.execute();

        new Thread(new Runnable() {
            @Override
            public void run() {
                SystemClock.sleep(10000);
            }
        }).start();
    }

    static class MyAsyncTask extends AsyncTask<Void, Void, Void> {
        private WeakReference<Context> weakReference;

        public MyAsyncTask(Context context) {
            weakReference = new WeakReference<>(context);
        }

        @Override
        protected Void doInBackground(Void... params) {
            SystemClock.sleep(10000);
            return null;
        }

        @Override
        protected void onPostExecute(Void aVoid) {
            super.onPostExecute(aVoid);
            ThreadLeakActivity activity = (ThreadLeakActivity) weakReference.get();
            if (activity != null) {
                //...
            }
        }
    }

    static class MyRunnable implements Runnable {
        @Override
        public void run() {
            SystemClock.sleep(10000);
        }
    }
}
5.Webview造成的內存泄漏
public class WebviewLeakActivity extends AppCompatActivity {
    private WebView mWebView;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        mWebView = (WebView) findViewById(R.id.wv_show);
        mWebView.loadUrl("http://www.baidu.com");
    }


    @Override
    protected void onDestroy() {
        destroyWebView();
        //解決
        //放在單獨進程中,退出殺掉進程
        android.os.Process.killProcess(android.os.Process.myPid());
        super.onDestroy();
    }

    private void destroyWebView() {
        if (mWebView != null) {
            mWebView.pauseTimers();
            mWebView.removeAllViews();
            mWebView.destroy();
            mWebView = null;
        }
    }

}
6.資源未關閉造成的內存泄漏
7.Bitmap內存泄漏,使用recycle方法回收
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章