首先咱們提出一個問題,什麼是內存泄漏? 內存泄漏,通俗得來說就是「沒有用的對象沒法被回收」
而後咱們再提出一個問題,內存泄露會致使什麼狀況?
確定是內存溢出,而後程序崩潰啊!html
相信初學者可能不太清楚內存溢出和內存泄漏的區別。java
要想了解內存泄露的知識,首先咱們要清楚如下的知識點android
如何判斷,只用記住一點:A類實例引用B類實例,而A類實例的生命週期長於B類實例的生命週期。git
在Android開發中,內存泄漏的地方仍是挺多的,有時候稍不注意就寫出了一個內存泄漏的代碼。因此說咱們要熟記哪些地方容易發生內存泄漏,在代碼Review的狀況下很容易檢查出來。github
單例模式使用的地方很是多,它的生命週期經常伴隨着App的一輩子,因此說也十分容易形成內存泄漏。
例如單例模式中引用Activity的Context,而單例模式的生命週期長於Activity。這裏單例模式引用Activity的實例,當Activity被銷燬,Activity沒法被回收,形成內存泄露。
算法
public class Single {
private static Single instance;
private Context context;
private Single(Context context) {
this.context = context;
}
public static Single getInstance(Context context) {
if (instance == null) {
instance = new Single(context);
}
return instance;
}
}
複製代碼
值得一提的是,若是這裏引用的Application的Context,將無任何影響。由於Application的生命週期與單例模式一樣長。bash
在靜態集合裏面添加對象,添加完成以後該集合將會一直引用此對象,該對象沒法被釋放。(不過咱們也寫不出這樣沙雕的代碼來!app
static List<Object> objectList = new ArrayList<>();
for (int i = 0; i < 10; i++) {
Object obj = new Object();
objectList.add(obj);
obj = null;
}
複製代碼
解決方法:在使用完該集合以後,將集合清空。ide
特色:匿名類和非靜態內部類都持有外部類的引用工具
匿名內部類引發的內存泄露,最典型的例子就是Handler泄漏。當Handler的消息沒有發送完畢,Activity就被銷燬了,此時Activity沒法被即時回收。
public class MainActivity extends Activity{
@SuppressLint("HandlerLeak")
private Handler handler = new Handler() {
@Override
public void handleMessage(Message msg) {
//do something...
}
};
@Override
public void onCreate(Bundle savedInstanceState){
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
}
}
複製代碼
如何解決Handler泄漏呢?咱們用static修飾Handler,這樣Hanlder的生命週期就與Activity無關了。若是想引用Activity實例,這裏能夠用一個弱引用來獲取。或者能夠在Activity 的onDestroy() 方法中移除全部的消息 handler.removeCallbacksAndMessages(null);
public class MainActivity extends Activity{
private final MyHandler handler = new MyHandler(this);
@Override
public void onCreate(Bundle savedInstanceState){
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
}
private static class MyHandler extends Handler {
private final WeakReference<MainActivity> mActivity;
public MyHandler(MainActivity activity) {
mActivity = new WeakReference<MainActivity>(activity);
}
@Override
public void handleMessage(Message msg) {
MainActivity activity = mActivity.get();
}
}
}
複製代碼
在Activity中new Thread時,若是在子線程作耗時操做,當Activity被銷燬後,子線程的工做並未完成,此時會內存泄漏。
public class MainActivity extends Activity{
@Override
public void onCreate(Bundle savedInstanceState){
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
new Thread(new Runnable() {
@Override
public void run() {
try {
Thread.sleep(100000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}).start();
}
}
複製代碼
這裏一樣能夠繼承 Thread 實現靜態內部類來解決。
若是成員變量被聲明爲 static,其生命週期將與整個app進程生命週期同樣。
在調用了流以後,必定要記得關閉流。用到流的地方通常都是文件操做,虛擬機沒法經過垃圾回收來釋放這些資源。
例如service忘記解除綁定,broadcastReceiver忘記解除訂閱,EventBus忘記解除訂閱等。
光憑肉眼咱們其實只能找出比較明顯的內存泄露點,還有許多隱藏得比較深的內存泄露。那麼咱們如何找到這些點呢?固然是利用工具。
在Android系統中,每一個App最多能分配大約只有100-200MB的內存空間,由於內存不夠,溢出而引發的程序崩潰仍是不在少數。因此說,平常開發中仍是要千萬注意內存泄露。