摘要:顯而易見的,當(dāng)這個是的時,就不存在內(nèi)存泄漏的問題。這個我在第一期自定義如何有效保證內(nèi)存泄漏問題已經(jīng)說得很明白了。
內(nèi)存泄漏 定義零零碎碎的東西總是記不長久,僅僅學(xué)習(xí)別人的文章也只是他人咀嚼后留下的殘?jiān)?。無意中發(fā)現(xiàn)了這個每日一道面試題,想了想如果只是簡單地去思考,那么不僅會收效甚微,甚至難一點(diǎn)的題目自己可能都懶得去想,堅(jiān)持不下來。所以不如把每一次的思考、理解以及別人的見解記錄下來。不僅加深自己的理解,更要激勵自己堅(jiān)持下去。
當(dāng)本應(yīng)該被釋放或無用的對象,因?yàn)楸黄渌婊畹膶ο蟪钟衅湟?,?dǎo)致該對象不能被垃圾回收器回收,一直占用著內(nèi)存,使程序運(yùn)行變得緩慢甚至崩潰。
原因為什么被其他存活的對象持有其引用,就不能被回收?這個就需要了解java的垃圾回收機(jī)制。
java垃圾回收機(jī)制
什么樣的對象會被認(rèn)為需要回收呢?我們現(xiàn)在將每一個對象看作有向圖的結(jié)點(diǎn),而對象之間的引用關(guān)系則是有向圖的邊。那么一定會有一個起始結(jié)點(diǎn)對象,如果這個對象是
方法區(qū)的類靜態(tài)屬性引用的對象
方法區(qū)中的常量引用的對象
本地方法棧中的native方法引用的對象
虛擬機(jī)棧(棧幀中的本地變量表(局部變量表))所引用的對象
那么由此對象可以在有向圖上遍歷到的所有對象都不會被回收。反之,就會被認(rèn)為是要回收的對象。
抽象的來說,一個程序中會存在許多這樣的有向圖,如果一個對象同時被兩個存在起始結(jié)點(diǎn)對象的有向圖所引用。當(dāng)一個有向圖完成使命,需要被銷毀,但另一個有向圖的生命周期還沒有結(jié)束。那么這個本應(yīng)該無用的對象,卻不能被垃圾回收器回收,只有當(dāng)另一個有向圖生命周期結(jié)束,才會被回收。
所以,就是我們常說的生命周期不同的兩個對象間有引用關(guān)系,生命周期短的可能會造成內(nèi)存泄漏,持續(xù)的時間取決于生命周期長的對象。如果這個對象是靜態(tài)變量,那么將會持續(xù)到整個程序運(yùn)行結(jié)束。
Android內(nèi)存泄漏情況 集合類一般的集合類并不會造成內(nèi)存泄漏,但是如果是全局性的集合類,如果不注意在使用完畢后進(jìn)行remove操作,就極有可能造成內(nèi)存泄露。
單例模式這里的單例模式是指創(chuàng)建時需要傳入Context作為參數(shù)。比如我們常寫的下面這個代碼。
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)鍵就在于這個Context,如果這個Context是Activity的Content,那么顯然Activity的生命周期和單例模式的對象的生命周期是不一樣的,傳入Content的Activity使用完畢需要被回收時,是無法被垃圾回收器回收的。
顯而易見的,當(dāng)這個Context是Application的時,就不存在內(nèi)存泄漏的問題。因?yàn)閱卫J降膶ο笈cApplication的生命周期都是整個應(yīng)用的生命周期,不會有任何問題。
所以,我們可以改為這樣寫
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也不是能隨便用的。如果是要啟動一個Activity,Application需要創(chuàng)建一個新的Task任務(wù)棧。而如果是創(chuàng)建一個Dialog,則只有Activity的context才可以。
匿名內(nèi)部類對于匿名內(nèi)部類,在Android中典型的例子就是Handler了吧。這個我在第一期---自定義Handler如何有效保證內(nèi)存泄漏問題已經(jīng)說得很明白了。主要就是匿名內(nèi)部類持有外部類的引用,匿名內(nèi)部類的一些操作使得該內(nèi)部類對象的生命周期和外部類的生命周期不相同,造成內(nèi)存泄漏。
非靜態(tài)內(nèi)部類在開發(fā)中,我們?yōu)榱顺绦虻母咝б约百Y源重復(fù)利用,我們可能會經(jīng)常寫出這樣的代碼。
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啟動時快速的使用這些資源,但卻會造成內(nèi)存泄漏。因?yàn)榉庆o態(tài)內(nèi)部類也默認(rèn)會持有外部類的引用。而由于這個非靜態(tài)內(nèi)部類的靜態(tài)實(shí)例,其生命周期會和整個應(yīng)用程序一樣長,所以會造成內(nèi)存泄露。
解決辦法就是將該內(nèi)部類設(shè)為靜態(tài)內(nèi)部類,或者把這個內(nèi)部類抽取出來封裝成一個單例模式。
資源未關(guān)閉在我們使用BroadcastReceiver、File、Course、Stream、ContentObserver等資源或者一些框架eventbus等明確表示需要Register與unRegister時,都應(yīng)該在Activity被銷毀時關(guān)閉或者注銷,否則這些資源將不會被回收。
不良代碼造成的壓力有時也并不是不能及時回收的對象造成的內(nèi)存泄漏,而是有些代碼沒有及時有效的釋放不需要使用的內(nèi)存,或者是沒有對于現(xiàn)有資源沒有有效利用而頻繁的申請新的內(nèi)存,造成內(nèi)存的巨大壓力。
比如ListView中的ContentView,不使用ViewHolder有效的復(fù)用View而頻繁的創(chuàng)建新的View,造成內(nèi)存壓力。
文章版權(quán)歸作者所有,未經(jīng)允許請勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。
轉(zhuǎn)載請注明本文地址:http://systransis.cn/yun/7047.html
摘要:但是如果你直接創(chuàng)建一個對象,然后重寫內(nèi)部方法,那么一定會提醒你會有內(nèi)存泄漏的可能。所以當(dāng)?shù)纳芷诮Y(jié)束后,而中還存在未處理的消息,那么上面一連串的引用關(guān)系就不允許的對象被回收,就造成了內(nèi)存泄漏。 零零碎碎的東西總是記不長久,僅僅學(xué)習(xí)別人的文章也只是他人咀嚼后留下的殘?jiān)?。無意中發(fā)現(xiàn)了這個每日一道面試題,想了想如果只是簡單地去思考,那么不僅會收效甚微,甚至難一點(diǎn)的題目自己可能都懶得去想,堅(jiān)持不下...
閱讀 769·2023-04-25 19:43
閱讀 4022·2021-11-30 14:52
閱讀 3855·2021-11-30 14:52
閱讀 3909·2021-11-29 11:00
閱讀 3839·2021-11-29 11:00
閱讀 3949·2021-11-29 11:00
閱讀 3613·2021-11-29 11:00
閱讀 6310·2021-11-29 11:00