摘要:顯而易見(jiàn)的,當(dāng)這個(gè)是的時(shí),就不存在內(nèi)存泄漏的問(wèn)題。這個(gè)我在第一期自定義如何有效保證內(nèi)存泄漏問(wèn)題已經(jīng)說(shuō)得很明白了。
內(nèi)存泄漏 定義零零碎碎的東西總是記不長(zhǎng)久,僅僅學(xué)習(xí)別人的文章也只是他人咀嚼后留下的殘?jiān)o(wú)意中發(fā)現(xiàn)了這個(gè)每日一道面試題,想了想如果只是簡(jiǎn)單地去思考,那么不僅會(huì)收效甚微,甚至難一點(diǎn)的題目自己可能都懶得去想,堅(jiān)持不下來(lái)。所以不如把每一次的思考、理解以及別人的見(jiàn)解記錄下來(lái)。不僅加深自己的理解,更要激勵(lì)自己堅(jiān)持下去。
當(dāng)本應(yīng)該被釋放或無(wú)用的對(duì)象,因?yàn)楸黄渌婊畹膶?duì)象持有其引用,導(dǎo)致該對(duì)象不能被垃圾回收器回收,一直占用著內(nèi)存,使程序運(yùn)行變得緩慢甚至崩潰。
原因為什么被其他存活的對(duì)象持有其引用,就不能被回收?這個(gè)就需要了解java的垃圾回收機(jī)制。
java垃圾回收機(jī)制
什么樣的對(duì)象會(huì)被認(rèn)為需要回收呢?我們現(xiàn)在將每一個(gè)對(duì)象看作有向圖的結(jié)點(diǎn),而對(duì)象之間的引用關(guān)系則是有向圖的邊。那么一定會(huì)有一個(gè)起始結(jié)點(diǎn)對(duì)象,如果這個(gè)對(duì)象是
方法區(qū)的類靜態(tài)屬性引用的對(duì)象
方法區(qū)中的常量引用的對(duì)象
本地方法棧中的native方法引用的對(duì)象
虛擬機(jī)棧(棧幀中的本地變量表(局部變量表))所引用的對(duì)象
那么由此對(duì)象可以在有向圖上遍歷到的所有對(duì)象都不會(huì)被回收。反之,就會(huì)被認(rèn)為是要回收的對(duì)象。
抽象的來(lái)說(shuō),一個(gè)程序中會(huì)存在許多這樣的有向圖,如果一個(gè)對(duì)象同時(shí)被兩個(gè)存在起始結(jié)點(diǎn)對(duì)象的有向圖所引用。當(dāng)一個(gè)有向圖完成使命,需要被銷(xiāo)毀,但另一個(gè)有向圖的生命周期還沒(méi)有結(jié)束。那么這個(gè)本應(yīng)該無(wú)用的對(duì)象,卻不能被垃圾回收器回收,只有當(dāng)另一個(gè)有向圖生命周期結(jié)束,才會(huì)被回收。
所以,就是我們常說(shuō)的生命周期不同的兩個(gè)對(duì)象間有引用關(guān)系,生命周期短的可能會(huì)造成內(nèi)存泄漏,持續(xù)的時(shí)間取決于生命周期長(zhǎng)的對(duì)象。如果這個(gè)對(duì)象是靜態(tài)變量,那么將會(huì)持續(xù)到整個(gè)程序運(yùn)行結(jié)束。
Android內(nèi)存泄漏情況 集合類一般的集合類并不會(huì)造成內(nèi)存泄漏,但是如果是全局性的集合類,如果不注意在使用完畢后進(jìn)行remove操作,就極有可能造成內(nèi)存泄露。
單例模式這里的單例模式是指創(chuàng)建時(shí)需要傳入Context作為參數(shù)。比如我們常寫(xiě)的下面這個(gè)代碼。
public class Manager {
private static Manager instance;
private Context context;
private Manager(Context context){
this.context = context;
}
public static Manager getInstance(Context context){
if(instance == null){
instance = new Manager(context);
}
return instance;
}
}
關(guān)鍵就在于這個(gè)Context,如果這個(gè)Context是Activity的Content,那么顯然Activity的生命周期和單例模式的對(duì)象的生命周期是不一樣的,傳入Content的Activity使用完畢需要被回收時(shí),是無(wú)法被垃圾回收器回收的。
顯而易見(jiàn)的,當(dāng)這個(gè)Context是Application的時(shí),就不存在內(nèi)存泄漏的問(wèn)題。因?yàn)閱卫J降膶?duì)象與Application的生命周期都是整個(gè)應(yīng)用的生命周期,不會(huì)有任何問(wèn)題。
所以,我們可以改為這樣寫(xiě)
public class Manager {
private static Manager instance;
private Context context;
private Manager(Context context){
this.context = context.getApplicationContext();
}
public static Manager getInstance(Context context){
if(instance == null){
instance = new Manager(context);
}
return instance;
}
}
當(dāng)然了,Application的Context也不是能隨便用的。如果是要啟動(dòng)一個(gè)Activity,Application需要?jiǎng)?chuàng)建一個(gè)新的Task任務(wù)棧。而如果是創(chuàng)建一個(gè)Dialog,則只有Activity的context才可以。
匿名內(nèi)部類對(duì)于匿名內(nèi)部類,在Android中典型的例子就是Handler了吧。這個(gè)我在第一期---自定義Handler如何有效保證內(nèi)存泄漏問(wèn)題已經(jīng)說(shuō)得很明白了。主要就是匿名內(nèi)部類持有外部類的引用,匿名內(nèi)部類的一些操作使得該內(nèi)部類對(duì)象的生命周期和外部類的生命周期不相同,造成內(nèi)存泄漏。
非靜態(tài)內(nèi)部類在開(kāi)發(fā)中,我們?yōu)榱顺绦虻母咝б约百Y源重復(fù)利用,我們可能會(huì)經(jīng)常寫(xiě)出這樣的代碼。
public class MainActivity extends BaseActivity {
private static Resource resource = null;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
if(resource == null){
resource = new Resource();
}
}
class Resource{
}
}
這樣做雖然有效的避免了資源的重復(fù)創(chuàng)建,每次在Activity啟動(dòng)時(shí)快速的使用這些資源,但卻會(huì)造成內(nèi)存泄漏。因?yàn)榉庆o態(tài)內(nèi)部類也默認(rèn)會(huì)持有外部類的引用。而由于這個(gè)非靜態(tài)內(nèi)部類的靜態(tài)實(shí)例,其生命周期會(huì)和整個(gè)應(yīng)用程序一樣長(zhǎng),所以會(huì)造成內(nèi)存泄露。
解決辦法就是將該內(nèi)部類設(shè)為靜態(tài)內(nèi)部類,或者把這個(gè)內(nèi)部類抽取出來(lái)封裝成一個(gè)單例模式。
資源未關(guān)閉在我們使用BroadcastReceiver、File、Course、Stream、ContentObserver等資源或者一些框架eventbus等明確表示需要Register與unRegister時(shí),都應(yīng)該在Activity被銷(xiāo)毀時(shí)關(guān)閉或者注銷(xiāo),否則這些資源將不會(huì)被回收。
不良代碼造成的壓力有時(shí)也并不是不能及時(shí)回收的對(duì)象造成的內(nèi)存泄漏,而是有些代碼沒(méi)有及時(shí)有效的釋放不需要使用的內(nèi)存,或者是沒(méi)有對(duì)于現(xiàn)有資源沒(méi)有有效利用而頻繁的申請(qǐng)新的內(nèi)存,造成內(nèi)存的巨大壓力。
比如ListView中的ContentView,不使用ViewHolder有效的復(fù)用View而頻繁的創(chuàng)建新的View,造成內(nèi)存壓力。
文章版權(quán)歸作者所有,未經(jīng)允許請(qǐng)勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。
轉(zhuǎn)載請(qǐng)注明本文地址:http://m.hztianpu.com/yun/7047.html
摘要:但是如果你直接創(chuàng)建一個(gè)對(duì)象,然后重寫(xiě)內(nèi)部方法,那么一定會(huì)提醒你會(huì)有內(nèi)存泄漏的可能。所以當(dāng)?shù)纳芷诮Y(jié)束后,而中還存在未處理的消息,那么上面一連串的引用關(guān)系就不允許的對(duì)象被回收,就造成了內(nèi)存泄漏。 零零碎碎的東西總是記不長(zhǎng)久,僅僅學(xué)習(xí)別人的文章也只是他人咀嚼后留下的殘?jiān)?。無(wú)意中發(fā)現(xiàn)了這個(gè)每日一道面試題,想了想如果只是簡(jiǎn)單地去思考,那么不僅會(huì)收效甚微,甚至難一點(diǎn)的題目自己可能都懶得去想,堅(jiān)持不下...
摘要:一面應(yīng)該還問(wèn)了其他內(nèi)容,但是兩次面試多線程面試問(wèn)題和答案采訪中,我們通常會(huì)遇到兩個(gè)主題采集問(wèn)題和多線程面試問(wèn)題。多線程是關(guān)于并發(fā)和線程的。我們正在共享重要的多線程面試問(wèn)題和答案。。 2016 年末,騰訊,百度,華為,搜狗和滴滴面試題匯總 2016 年未,騰訊,百度,華為,搜狗和滴滴面試題匯總 【碼農(nóng)每日一題】Java 內(nèi)部類(Part 2)相關(guān)面試題 關(guān)注一下嘛,又不讓你背鍋!問(wèn):Ja...
摘要:一面應(yīng)該還問(wèn)了其他內(nèi)容,但是兩次面試多線程面試問(wèn)題和答案采訪中,我們通常會(huì)遇到兩個(gè)主題采集問(wèn)題和多線程面試問(wèn)題。多線程是關(guān)于并發(fā)和線程的。我們正在共享重要的多線程面試問(wèn)題和答案。。 2016 年末,騰訊,百度,華為,搜狗和滴滴面試題匯總 2016 年未,騰訊,百度,華為,搜狗和滴滴面試題匯總 【碼農(nóng)每日一題】Java 內(nèi)部類(Part 2)相關(guān)面試題 關(guān)注一下嘛,又不讓你背鍋!問(wèn):Ja...
閱讀 847·2023-04-25 19:43
閱讀 4115·2021-11-30 14:52
閱讀 3930·2021-11-30 14:52
閱讀 4027·2021-11-29 11:00
閱讀 3921·2021-11-29 11:00
閱讀 4039·2021-11-29 11:00
閱讀 3769·2021-11-29 11:00
閱讀 6607·2021-11-29 11:00