Android AndroidStudio MAT LeakCanary 內存分析之 初識內存泄漏
http://blog.csdn.net/qq_28195645/article/details/51733342
Android AndroidStudio MAT LeakCanary 內存分析之 AndroidStudio 內存泄漏分析 Memory Monitor
http://blog.csdn.net/qq_28195645/article/details/51734506
Android AndroidStudio MAT LeakCanary 內存分析之 LeakCanary
http://blog.csdn.net/qq_28195645/article/details/51734987
Android AndroidStudio MAT LeakCanary 內存分析之 DDMS+MAT
http://blog.csdn.net/qq_28195645/article/details/51735522
說起Android進階、那麼必須談一談內存泄漏。那麼現在檢測app內存泄漏的辦法有很多 比如 :
1、DDMS + MAT http://www.eclipse.org/mat/downloads.php
2、leakcanary https://github.com/square/leakcanary
3、AndroidStudio 1.5+ Memory Monitor https://developer.android.com/studio/profile/am-memory.html
題外:Android性能分析工具——TraceView
實際開發中、常用的就是leakcanary,本系列我們會將這三種辦法都有涉及
首先什麼是內存泄漏、常引起內存泄漏的code
如上圖、如果你要刪除對象B,這也將釋放它所支配的對象(C,D,E,F)所使用的內存.事實上,如果對象C,D,E和F被標記爲刪除,但對象B還是指他們,這就是他們沒有釋放的原因。
Java內存泄漏指的是進程中某些對象(垃圾對象)已經沒有使用價值了,但是它們卻可以直接或間接地引用到gc roots導致無法被GC回收。無用的對象佔據着內存空間,使得實際可使用內存變小,形象地說法就是內存泄漏了。而內存泄漏出現的原因就是存在了無效的引用,導致本來需要被GC的對象沒有被回收掉。這樣會導致一直佔用着內存。下面分析一些可能導致內存泄漏的情景。如果想了解更多就百度吧
1、非靜態內部類的靜態實例容易造成內存泄漏
private static Leak mLeak;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_second);
mLeak = new Leak();
}
class Leak {
}
上面的代碼中的mLeak實例類型爲靜態實例,在第一個MainActivity act1實例創建時,mLeak會獲得並一直持有act1的引用。(原因:mLeak是存儲在靜態區的靜態變量,而Leak是內部類,其持有外部類Activity的引用。)當MainAcitivity銷燬後重建,因爲mLeak持有act1的引用,所以act1是無法被GC回收的,進程中會存在2個MainActivity實例(act1和重建後的MainActivity實例),這個act1對象就是一個無用的但一直佔用內存的對象,即無法回收的垃圾對象。所以,對於lauchMode不是singleInstance的Activity, 應該避免在activity裏面實例化其非靜態內部類的靜態實例。
2、activity使用靜態成員
private static Drawable sBackground;
@Override
protected void onCreate(Bundle state) {
super.onCreate(state);
TextView label = new TextView(this);
label.setText("Leaks are bad");
if (sBackground == null) {
sBackground = getDrawable(R.drawable.large_bitmap);
}
label.setBackgroundDrawable(sBackground);
setContentView(label);
}
由於用靜態成員sBackground 緩存了drawable對象,所以activity加載速度會加快,但是這樣做是錯誤的。因爲在Android 2.3系統上,它會導致activity銷燬後無法被系統回收。
label .setBackgroundDrawable函數調用會將label賦值給sBackground的成員變量mCallback。
上面代碼意味着:sBackground(GC Root)會持有TextView對象,而TextView持有Activity對象。所以導致Activity對象無法被系統回收。
下面看看android4.0爲了避免上述問題所做的改進。
先看看android 2.3的Drawable.Java對setCallback的實現:
public final void setCallback(Callback cb){
mCallback = cb;
}
再看看android 4.0的Drawable.Java對setCallback的實現:
public final void setCallback(Callback cb){
mCallback = newWeakReference<Callback> (cb);
}
在android 2.3中要避免內存泄漏也是可以做到的, 在activity的onDestroy時調用
sBackgroundDrawable.setCallback(null)。
以上2個例子的內存泄漏都是因爲Activity的引用的生命週期超越了activity對象的生命週期。也就是常說的Context泄漏,因爲activity就是context。
想要避免context相關的內存泄漏,需要注意以下幾點:
·不要對activity的context長期引用(一個activity的引用的生存週期應該和activity的生命週期相同)
·如果可以的話,儘量使用關於application的context來替代和activity相關的context
·如果一個acitivity的非靜態內部類的生命週期不受控制,那麼避免使用它;正確的方法是使用一個靜態的內部類,並且對它的外部類有一WeakReference,就像在ViewRootImpl中內部類W所做的那樣。
3、使用handler時的內存問題
public class HandlerActivity extends Activity {
private final Handler mHandler = new Handler() {
@Override
public void handleMessage(Message msg) {
// ...
}
};
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
mHandler.sendMessageDelayed(Message.obtain(), 60000);
//just finish this activity
finish();
}
}
以上的寫法會出現這樣的error提示:This Handler class should be static or leaks might occur
1、Handler 的生命週期與Activity 不一致
- 當Android應用啓動的時候,會先創建一個UI主線程的Looper對象,Looper實現了一個簡單的消息隊列,一個一個的處理裏面的Message對象。主線程Looper對象在整個應用生命週期中存在。
- 當在主線程中初始化Handler時,該Handler和Looper的消息隊列關聯(沒有關聯會報錯的)。發送到消息隊列的Message會引用發送該消息的Handler對象,這樣系統可以調用 Handler#handleMessage(Message) 來分發處理該消息。
2、handler 引用 Activity 阻止了GC對Acivity的回收
在Java中,非靜態(匿名)內部類會默認隱性引用外部類對象。而靜態內部類不會引用外部類對象。
如果外部類是Activity,則會引起Activity泄露 。
當Activity finish後,延時消息會繼續存在主線程消息隊列中1分鐘,然後處理消息。而該消息引用了Activity的Handler對象,然後這個Handler又引用了這個Activity。這些引用對象會保持到該消息被處理完,這樣就導致該Activity對象無法被回收,從而導致了上面說的 Activity泄露。
避免handler導致的內存泄漏
- 使用顯形的引用,1.靜態內部類。 2. 外部類
- 使用弱引用 2. WeakReference
- android-weak-handler https://github.com/badoo/android-weak-handler
- Handler可以使用removeCallbacksAndMessages(null),它將移除這個Handler所擁有的Runnable與Message。
修改代碼如下
public class HandlerActivity2 extends Activity {
private static final int MESSAGE_1 = 1;
private static final int MESSAGE_2 = 2;
private static final int MESSAGE_3 = 3;
private final Handler mHandler = new MyHandler(this);
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
mHandler.sendMessageDelayed(Message.obtain(), 60000);
// just finish this activity
finish();
}
public void todo() {
};
private static class MyHandler extends Handler {
private final WeakReference<HandlerActivity2> mActivity;
public MyHandler(HandlerActivity2 activity) {
mActivity = new WeakReference<HandlerActivity2>(activity);
}
@Override
public void handleMessage(Message msg) {
System.out.println(msg);
if (mActivity.get() == null) {
return;
}
mActivity.get().todo();
}
}
/**
* 當Activity finish後 handler對象還是在Message中排隊。 還是會處理消息,這些處理有必要?
*正常Activitiy finish後,已經沒有必要對消息處理,那需要怎麼做呢?
*解決方案也很簡單,在Activity onStop或者onDestroy的時候,取消掉該Handler對象的Message和 Runnable。
*通過查看Handler的API,它有幾個方法:removeCallbacks(Runnable r)和removeMessages(int what)等。
*/
@Override
public void onDestroy() {
// If null, all callbacks and messages will be removed.
mHandler.removeCallbacksAndMessages(null);
}
}