??內(nèi)存泄漏(Memory Leak)是指程序中己動(dòng)態(tài)分配的堆內(nèi)存由于某種原因程序未釋放或無(wú)法釋放,造成系統(tǒng)內(nèi)存的浪費(fèi)篙挽,導(dǎo)致程序運(yùn)行速度減慢甚至系統(tǒng)崩潰等嚴(yán)重后果。
檢測(cè)工具
??其實(shí),這個(gè)神秘又實(shí)用的工具就在我們的AS中纵苛,簡(jiǎn)單暴力,運(yùn)行App就可以對(duì)其進(jìn)行內(nèi)存分析,它就是Android Profiler攻人,打開(kāi)步驟:View-Tools Window-Profiler取试,對(duì)于它的使用,建議朋友們查看這篇文章使用 Memory Profiler 查看 Java 堆和內(nèi)存分配怀吻,因?yàn)榫W(wǎng)上很多文章都來(lái)源于此處瞬浓,多看幾遍才能體會(huì)到其中的樂(lè)趣!
手動(dòng)GC的使用
1烙博、創(chuàng)建一個(gè)新應(yīng)用-打開(kāi)Profiler-運(yùn)行
2瑟蜈、新增一個(gè)HandlerActivity且在MainActivity中點(diǎn)擊按鈕打開(kāi)該Activity-返回鍵
??這段時(shí)間內(nèi),新增了一個(gè)實(shí)例HandlerActivity渣窜,它只有分配內(nèi)存的時(shí)間铺根,沒(méi)有釋放時(shí)間,雖然我們按了返回鍵乔宿,但是它的內(nèi)存還沒(méi)有被GC回收位迂,所以也就沒(méi)有釋放內(nèi)存。運(yùn)行一段時(shí)間后详瑞,該對(duì)象還是沒(méi)有得到釋放
??我們點(diǎn)擊一下手動(dòng)GC按鈕看看掂林,手動(dòng)GC按鈕在這個(gè)地方
??這個(gè)時(shí)候就發(fā)現(xiàn)釋放時(shí)間就出來(lái)了,有沒(méi)有一種雨后天晴的快感坝橡,通過(guò)這個(gè)例子相信大家都能夠使用該工具分析內(nèi)存泄漏了泻帮。
小結(jié):Activity執(zhí)行onDestroy方法后,不是立刻釋放內(nèi)存的
Handler內(nèi)存泄漏分析
1计寇、匿名內(nèi)部類寫(xiě)法
public class HandlerActivity extends AppCompatActivity {
private Handler handler = new Handler() {
@Override
public void handleMessage(Message msg) {
super.handleMessage(msg);
}
};
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_handler);
handler.sendEmptyMessageDelayed(0, 24 * 60 * 60 * 1000);
}
}
??網(wǎng)上都說(shuō)這個(gè)Handler匿名類持有外部類的引用會(huì)導(dǎo)致內(nèi)存泄漏锣杂,我們踹一踹,打開(kāi)HandlerActivity后,點(diǎn)擊返回鍵番宁,返回到MainActivity,并且點(diǎn)擊手動(dòng)GC按鈕幾次!
??通過(guò)實(shí)驗(yàn)元莫,我們得出的確如此,無(wú)論手動(dòng)GC多少次蝶押,該對(duì)象占用的內(nèi)存都不會(huì)得到釋放踱蠢,不過(guò)這個(gè)跟延遲時(shí)間有關(guān),我們這里設(shè)置的延遲時(shí)間是一天棋电,因此茎截,一天內(nèi)不管你怎么手動(dòng)回收,系統(tǒng)都不會(huì)釋放掉這塊內(nèi)存赶盔。如果我們把延時(shí)發(fā)送時(shí)間改為十秒稼虎,那么十秒內(nèi)內(nèi)存也不會(huì)得到釋放,十秒后手動(dòng)GC招刨,這塊內(nèi)存就會(huì)釋放掉了
小結(jié):
1霎俩、匿名內(nèi)部類持有外部類引用(外部類釋放不了可證明)
2哀军、使用匿名Handler不一定會(huì)導(dǎo)致內(nèi)存泄漏(跟延遲時(shí)間有關(guān))
3、延時(shí)發(fā)送時(shí)間越長(zhǎng)打却,泄漏的時(shí)間也就越長(zhǎng)
2杉适、靜態(tài)內(nèi)部類寫(xiě)法
public class HandlerActivity extends AppCompatActivity {
private MLHandler handler = new MLHandler();
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_handler);
handler.sendEmptyMessageDelayed(0, 24 * 60 * 60 * 1000);
}
static class MLHandler extends Handler {
@Override
public void handleMessage(Message msg) {
super.handleMessage(msg);
}
}
}
小結(jié)
1、靜態(tài)內(nèi)部類未持有外部類引用
2柳击、HandlerActivity已釋放猿推,MLHandler不能釋放(跟延遲時(shí)間有關(guān))
3、外部類寫(xiě)法
HandlerActivity.java
public class HandlerActivity extends AppCompatActivity {
private MLHandler handler=new MLHandler();
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_handler);
handler.sendEmptyMessageDelayed(0, 24 * 60 * 60 * 1000);
}
}
MLHandler.java
class MLHandler extends Handler {
@Override
public void handleMessage(Message msg) {
super.handleMessage(msg);
}
}
內(nèi)存分析
??HandlerActivity已經(jīng)被手動(dòng)GC捌肴,但是MLHandler還是無(wú)法得到釋放蹬叭,不知道是不是延遲太長(zhǎng)導(dǎo)致?我們把延遲時(shí)間 handler.sendEmptyMessageDelayed(0, 24 * 60 * 60 * 1000)状知,改成一秒handler.sendEmptyMessageDelayed(0, 1 * 60 * 1000)秽五,結(jié)果發(fā)現(xiàn)一秒內(nèi)怎么點(diǎn)手動(dòng)回收,MLHandler都不會(huì)釋放饥悴,一秒后點(diǎn)擊GC按鈕就立馬見(jiàn)效了坦喘!
Handler總結(jié)
1、匿名內(nèi)部類寫(xiě)法會(huì)持有外部類引用西设,但是不一樣會(huì)導(dǎo)致內(nèi)存泄漏
2瓣铣、是否發(fā)生內(nèi)存泄漏與延遲時(shí)間有關(guān)
3、外部類寫(xiě)法和靜態(tài)內(nèi)部類一樣贷揽,也會(huì)導(dǎo)致內(nèi)存泄漏棠笑,和匿名內(nèi)部類的區(qū)別在于不會(huì)持有外部類的引用
單例模式持有Activity內(nèi)存泄漏分析
SingleInstanceActivity.java
public class SingleInstanceActivity extends AppCompatActivity {
@Override
protected void onCreate(@Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_single_instance);
SingleInstance.getInstance(this);
}
}
SingleInstance.java
public class SingleInstance {
private static Context context;
private static SingleInstance singleInstance;
private SingleInstance() {
}
public static SingleInstance getInstance(Context ctx) {
if (singleInstance == null) {
context = ctx;
singleInstance = new SingleInstance();
}
return singleInstance;
}
}
??通過(guò)實(shí)驗(yàn)發(fā)現(xiàn),手動(dòng)GC好幾次禽绪,但是因?yàn)閱卫纳芷谂c應(yīng)用一致蓖救,導(dǎo)致SingleInstance實(shí)例一直存在,SingleInstanceActivity釋放不了是因?yàn)镾ingleInstance持有它的引用丐一。
解決方案
由于單例生命周期與應(yīng)用一致,因此我們把ApplicationContext給它就可以了
context = ctx;
換成
context = ctx.getApplicationContext();
??通過(guò)上面這些例子淹冰,我們學(xué)會(huì)了使用工具Android Profiler對(duì)內(nèi)存進(jìn)行分析库车,并對(duì)常見(jiàn)的內(nèi)存泄漏典型案例做了相關(guān)實(shí)驗(yàn),相信朋友們已經(jīng)有了深刻的認(rèn)識(shí)樱拴,以后自己也可以對(duì)應(yīng)用做內(nèi)存泄漏檢測(cè)了柠衍。