Android內(nèi)存泄露

內(nèi)存泄露指的是該釋放的對象沒有釋放角钩,一直被某個或某些實例特持有卻不再被使用導致GC不能回收骚灸。
首先司抱,我們先看看Java是怎樣來分配內(nèi)存的:

Java內(nèi)存分配策略

靜態(tài)分配

靜態(tài)分配使用靜態(tài)存儲區(qū)(方法區(qū)),主要存放靜態(tài)數(shù)據(jù)驻呐、全局static數(shù)據(jù)和常量毁渗。這塊內(nèi)存在程序編譯時就已經(jīng)分配好践磅,并且在程序整個運行期間都存在。

棧式分配

棧式分配使用棧區(qū)灸异。當方法被執(zhí)行時府适,方法體內(nèi)的局部變量(其中包括基礎數(shù)據(jù)類型羔飞、對象的引用)都在棧上創(chuàng)建,并在方法執(zhí)行結(jié)束時這些局部變量所持有的內(nèi)存將會自動被釋放檐春。因為棧內(nèi)存分配運算內(nèi)置于處理器的指令集中逻淌,效率很高,但是分配的內(nèi)存容量有限喇聊。

堆式分配

堆式分配使用堆區(qū)恍风。堆區(qū)又稱動態(tài)內(nèi)存分配,通常就是指在程序運行時直接 new 出來的內(nèi)存誓篱,也就是對象的實例。這部分內(nèi)存在不使用時將會由 Java 垃圾回收器來負責回收凯楔。

所以窜骄,Java內(nèi)存泄漏的根本原因就是:
長生命周期的對象持有短生命周期對象的引用就很可能發(fā)生內(nèi)存泄漏,盡管短生命周期對象已經(jīng)不再需要摆屯,但是因為長生命周期持有它的引用而導致不能被回收邻遏,這就是Java中內(nèi)存泄漏的發(fā)生場景,而內(nèi)存泄漏的區(qū)域就是堆區(qū)虐骑。

下面介紹Java和Android中引起內(nèi)存泄露的常見情況

靜態(tài)集合類引起內(nèi)存泄漏

像HashMap准验、Vector等的使用最容易出現(xiàn)內(nèi)存泄露,如果是靜態(tài)變量廷没,這些靜態(tài)變量的生命周期和應用程序一致糊饱,他們所引用的所有的對象Object也不能被釋放,因為他們也將一直被Vector等引用著颠黎。如下程序另锋,如果我們僅僅釋放引用本身,那么 Vector 仍然引用該對象狭归,所以這個對象對 GC 來說是不可回收的夭坪。因此,如果對象加入到Vector 后过椎,還必須從 Vector 中刪除室梅,最簡單的方法就是將 Vector 對象設置為 null。
在Android中疚宇,集合類如果僅僅有添加元素的方法亡鼠,而沒有相應的刪除機制,導致內(nèi)存被占用灰嫉。如果這個集合類是全局性的變量 (比如類中的靜態(tài)屬性拆宛,全局性的 map 等即有靜態(tài)引用或 final 一直指向它),那么沒有相應的刪除機制讼撒,很可能導致集合所占用的內(nèi)存只增不減浑厚。

Static Vector v = new Vector(10);
for (int i = 1; i<100; i++)
{
Object o = new Object();
v.add(o);
}

當集合里面的對象屬性被修改后股耽,再調(diào)用remove()方法時不起作用

如下程序:

public static void main(String[] args)
{
    Set<Person> set = new HashSet<Person>();
    Person p1 = new Person("唐僧","pwd1",25);
    Person p2 = new Person("孫悟空","pwd2",26);
    Person p3 = new Person("豬八戒","pwd3",27);
    set.add(p1);
    set.add(p2);
    set.add(p3);
    System.out.println("總共有:"+set.size()+" 個元素!"); //結(jié)果:總共有:3 個元素!
    p3.setAge(2); //修改p3的年齡,此時p3元素對應的hashcode值發(fā)生改變
    set.remove(p3); //此時remove不掉,造成內(nèi)存泄漏
    set.add(p3); //重新添加钳幅,居然添加成功
    System.out.println("總共有:"+set.size()+" 個元素!"); //結(jié)果:總共有:4 個元素!
    for (Person person : set)
    {
        System.out.println(person);
    }
}

監(jiān)聽器沒有釋放造成泄露

調(diào)用一個控件的諸如addXXXListener()等方法來增加監(jiān)聽器物蝙,但往往在釋放對象的時候卻沒有記住去刪除這些監(jiān)聽器,從而增加了內(nèi)存泄漏的機會敢艰。
在Android中的情況如下
系統(tǒng)服務可以通過Context.getSystemService 獲取诬乞,它們負責執(zhí)行某些后臺任務,或者為硬件訪問提供接口钠导。如果Context 對象想要在服務內(nèi)部的事件發(fā)生時被通知震嫉,那就需要把自己注冊到服務的監(jiān)聽器中。然而牡属,這會讓服務持有Activity 的引用票堵,如果在Activity onDestory時沒有釋放掉引用就會內(nèi)存泄漏。
解決方案:
1逮栅、使用ApplicationContext代替ActivityContext:

mSensorManager = (SensorManager) this.getSystemService(Context.SENSOR_SERVICE);

改成

mSensorManager = (SensorManager) getApplicationContext().getSystemService(Context.SENSOR_SERVICE);

2悴势、在Activity執(zhí)行onDestory時,調(diào)用反注冊;

protected void onDetachedFromWindow() {        
    if (this.mActionShell != null) {        this.mActionShell.setOnClickListener((OnAreaClickListener)null);
    }        
    if (this.mButtonShell != null) { 
        this.mButtonShell.setOnClickListener((OnAreaClickListener)null);
    }        
    if (this.mCountShell != this.mCountShell) {        this.mCountShell.setOnClickListener((OnAreaClickListener)null);
    }        
    super.onDetachedFromWindow();
}

各種連接

比如數(shù)據(jù)庫連接(dataSourse.getConnection())措伐,網(wǎng)絡連接(socket)和io連接特纤,除非其顯式的調(diào)用了其close()方法將其連接關閉,否則是不會自動被GC 回收的侥加。對于Resultset 和Statement 對象可以不進行顯式回收捧存,但Connection 一定要顯式回收,因為Connection 在任何時候都無法自動回收官硝,而Connection一旦回收矗蕊,Resultset 和Statement 對象就會立即為NULL。但是如果使用連接池氢架,情況就不一樣了傻咖,除了要顯式地關閉連接,還必須顯式地關閉Resultset Statement 對象(關閉其中一個岖研,另外一個也會關閉)卿操,否則就會造成大量的Statement 對象無法釋放,從而引起內(nèi)存泄漏孙援。這種情況下一般都會在try里面去的連接害淤,在finally里面釋放連接。

內(nèi)部類和外部模塊的引用

內(nèi)部類的引用是比較容易遺忘的一種拓售,而且一旦沒釋放可能導致一系列的后繼類對象沒有釋放窥摄。此外程序員還要小心外部模塊不經(jīng)意的引用,例如程序員A 負責A 模塊础淤,調(diào)用了B 模塊的一個方法如: public void registerMsg(Object b); 這種調(diào)用就要非常小心了崭放,傳入了一個對象哨苛,很可能模塊B就保持了對該對象的引用,這時候就需要注意模塊B 是否提供相應的操作去除引用币砂。
在Android中建峭,非靜態(tài)內(nèi)部類也很容易引起的內(nèi)存泄露:
有的時候我們可能會在啟動頻繁的Activity中,為了避免重復創(chuàng)建相同的數(shù)據(jù)資源决摧,可能會出現(xiàn)這種寫法:

        public class MainActivity extends AppCompatActivity {
                private static TestResource mResource = null;
                @Override
                protected void onCreate(Bundle savedInstanceState) {
                        super.onCreate(savedInstanceState);
                        setContentView(R.layout.activity_main);
                        if(mManager == null){
                                mManager = new TestResource();
                }
                //...
        }
        class TestResource {
        //...
        }
        }

這樣就在Activity內(nèi)部創(chuàng)建了一個非靜態(tài)內(nèi)部類的單例亿蒸,每次啟動Activity時都會使用該單例的數(shù)據(jù),這樣雖然避免了資源的重復創(chuàng)建掌桩,不過這種寫法卻會造成內(nèi)存泄漏边锁,因為非靜態(tài)內(nèi)部類默認會持有外部類的引用,而該非靜態(tài)內(nèi)部類又創(chuàng)建了一個靜態(tài)的實例波岛,該實例的生命周期和應用的一樣長砚蓬,這就導致了該靜態(tài)實例一直會持有該Activity的引用,導致Activity的內(nèi)存資源不能正撑枭回收。
正確的做法:
將該內(nèi)部類設為靜態(tài)內(nèi)部類或?qū)⒃搩?nèi)部類抽取出來封裝成一個單例祟剔,如果需要使用Context隔躲,請按照上面推薦的使用Application 的 Context(當然,Application 的 context 不是萬能的物延,所以也不能隨便亂用宣旱,對于有些地方則必須使用 Activity 的 Context)。

android開發(fā)經(jīng)常會繼承實現(xiàn)Activity/Fragment/View叛薯,此時如果你使用了匿名類浑吟,并被異步線程持有了,那要小心了耗溜,如果沒有任何措施這樣一定會導致泄露:

    public class MainActivity extends Activity {
    ...
    Runnable ref1 = new MyRunable();
    Runnable ref2 = new Runnable() {
        @Override
        public void run() {
        }
    };
       ...
    }

ref1和ref2的區(qū)別是组力,ref2使用了匿名內(nèi)部類。我們來看看運行時這兩個引用的內(nèi)存:


1.png

可以看到抖拴,ref1沒什么特別的燎字。但ref2這個匿名類的實現(xiàn)對象里面多了一個引用:this$0這個引用指向MainActivity.this,也就是說當前的MainActivity實例會被ref2持有阿宅,如果將這個引用再傳入一個異步線程候衍,此線程和此Acitivity生命周期不一致的時候,就造成了Activity的泄露洒放。

單例模式引起的內(nèi)存泄露

不正確使用單例模式是引起內(nèi)存泄漏的一個常見問題蛉鹿,單例對象在初始化后將在JVM的整個生命周期中存在(以靜態(tài)變量的方式),如果單例對象持有外部的引用往湿,那么這個對象將不能被JVM正逞欤回收惋戏,導致內(nèi)存泄漏。
如下程序:顯然B采用singleton模式随闺,它持有一個A對象的引用日川,而這個A類的對象將不能被回收。

class A{
public A(){
B.getInstance().setA(this);
}
....
}
//B類采用單例模式
class B{
    private A a;
    private static B instance=new B();

    public B(){}

    public static B getInstance(){
        return instance;
}

public void setA(A a){
  this.a=a;
}
  //getter...
} 

在Android中矩乐,單例類必須特別注意context的使用龄句。由于單例的靜態(tài)特性使得其生命周期跟應用的生命周期一樣長,所以如果使用不恰當?shù)脑捝⒑保苋菀自斐蓛?nèi)存泄漏.
如下程序:

public class AppManager {
    private static AppManager instance;
    private Context context;
    private AppManager(Context context) {
        this.context = context;
    }
    public static AppManager getInstance(Context context) {
        if (instance == null) {
            instance = new AppManager(context);
        }
        return instance;
    }
}

這是一個普通的單例模式分歇,當創(chuàng)建這個單例的時候,由于需要傳入一個Context欧漱,所以這個Context的生命周期的長短至關重要:
1职抡、如果此時傳入的是 Application 的 Context,因為 Application 的生命周期就是整個應用的生命周期误甚,所以這將沒有任何問題缚甩。
2、如果此時傳入的是 Activity 的 Context窑邦,當這個 Context 所對應的 Activity 退出時擅威,由于該 Context 的引用被單例對象所持有,其生命周期等于整個應用程序的生命周期冈钦,所以當前 Activity 退出時它的內(nèi)存并不會被回收郊丛,這就造成泄漏了。

資源性對象Cursor瞧筛,Stream沒有close厉熟,View沒有recyle

資源性對象比如(Cursor,F(xiàn)ile文件等)往往都用了一些緩沖较幌,我們在不使用的時候揍瑟,應該及時關閉它們,以便它們的緩沖及時回收內(nèi)存绅络。它們的緩沖不僅存在于 java虛擬機內(nèi)月培,還存在于java虛擬機外。如果我們僅僅是把它的引用設置為null,而不關閉它們恩急,往往會造成內(nèi)存泄漏杉畜。因為有些資源性對象,比如SQLiteCursor(在析構(gòu)函數(shù)finalize(),如果我們沒有關閉它衷恭,它自己會調(diào)close()關閉)此叠,如果我們沒有關閉它,系統(tǒng)在回收它時也會關閉它随珠,但是這樣的效率太低了灭袁。因此對于資源性對象在不使用的時候猬错,應該調(diào)用它的close()函數(shù),將其關閉掉茸歧,然后才置為null. 在我們的程序退出時一定要確保我們的資源性對象已經(jīng)關閉倦炒。

構(gòu)造Adapter時,沒有使用緩存的ConvertView

初始時ListView會從Adapter中根據(jù)當前的屏幕布局實例化一定數(shù)量的View對象软瞎,同時ListView會將這些View對象 緩存起來逢唤。
當向上滾動ListView時,原先位于最上面的List Item的View對象會被回收涤浇,然后被用來構(gòu)造新出現(xiàn)的最下面的List Item鳖藕。
這個構(gòu)造過程就是由getView()方法完成的,getView()的第二個形參View ConvertView就是被緩存起來的List Item的View對象(初始化時緩存中沒有View對象則ConvertView是null)只锭。

Handler 造成的內(nèi)存泄漏

為了避免 ANR 而不在主線程進行耗時操作著恩,在處理網(wǎng)絡任務或者封裝一些請求回調(diào)等api都借助Handler來處理,但 Handler 不是萬能的蜻展,對于 Handler 的使用代碼編寫一不規(guī)范即有可能造成內(nèi)存泄漏喉誊。另外,我們知道 Handler纵顾、Message 和 MessageQueue 都是相互關聯(lián)在一起的裹驰,萬一 Handler 發(fā)送的 Message 尚未被處理,則該 Message 及發(fā)送它的 Handler 對象將被線程 MessageQueue 一直持有片挂。由于 Handler 屬于 TLS(Thread Local Storage) 變量, 生命周期和 Activity 是不一致的。因此這種實現(xiàn)方式一般很難保證跟 View 或者 Activity 的生命周期保持一致贞盯,故很容易導致無法正確釋放音念。

    public class SampleActivity extends Activity {
    private final Handler mLeakyHandler = new Handler() {
    @Override
    public void handleMessage(Message msg) {
      // ...
    }
    }
    @Override
    protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    // Post a message and delay its execution for 10 minutes.
    mLeakyHandler.postDelayed(new Runnable() {
      @Override
      public void run() { /* ... */ }
    }, 1000 * 60 * 10);
    // Go back to the previous Activity.
    finish();
    }
    }

在該 SampleActivity 中聲明了一個延遲10分鐘執(zhí)行的消息 Message,mLeakyHandler 將其 push 進了消息隊列 MessageQueue 里躏敢。當該 Activity 被 finish() 掉時闷愤,延遲執(zhí)行任務的 Message 還會繼續(xù)存在于主線程中,它持有該 Activity 的 Handler 引用件余,所以此時 finish() 掉的 Activity 就不會被回收了從而造成內(nèi)存泄漏(因 Handler 為非靜態(tài)內(nèi)部類讥脐,它會持有外部類的引用,在這里就是指 SampleActivity)啼器。
修復方法:在 Activity 中避免使用非靜態(tài)內(nèi)部類旬渠,比如上面我們將 Handler 聲明為靜態(tài)的,則其存活期跟 Activity 的生命周期就無關了端壳。同時通過弱引用的方式引入 Activity告丢,避免直接將 Activity 作為 context 傳進去:

public class SampleActivity extends Activity {
  /**
   * Instances of static inner classes do not hold an implicit
   * reference to their outer class.
   */
  private static class MyHandler extends Handler {
    private final WeakReference<SampleActivity> mActivity;
    public MyHandler(SampleActivity activity) {
      mActivity = new WeakReference<SampleActivity>(activity);
    }
    @Override
    public void handleMessage(Message msg) {
      SampleActivity activity = mActivity.get();
      if (activity != null) {
        // ...
      }
    }
  }
  private final MyHandler mHandler = new MyHandler(this);
  /**
   * Instances of anonymous classes do not hold an implicit
   * reference to their outer class when they are "static".
   */
  private static final Runnable sRunnable = new Runnable() {
      @Override
      public void run() { /* ... */ }
  };
  @Override
  protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    // Post a message and delay its execution for 10 minutes.
    mHandler.postDelayed(sRunnable, 1000 * 60 * 10);
    // Go back to the previous Activity.
    finish();
  }
}

綜述,即推薦使用靜態(tài)內(nèi)部類 + WeakReference 這種方式损谦。每次使用前注意判空岖免。
創(chuàng)建一個靜態(tài)Handler內(nèi)部類岳颇,然后對 Handler 持有的對象使用弱引用,這樣在回收時也可以回收 Handler 持有的對象颅湘,但是這樣做雖然避免了 Activity 泄漏话侧,不過 Looper 線程的消息隊列中還是可能會有待處理的消息,所以我們在 Activity 的 Destroy 時或者 Stop 時應該移除消息隊列 MessageQueue 中的消息闯参。
下面幾個方法都可以移除 Message:

public final void removeCallbacks(Runnable r);
public final void removeCallbacks(Runnable r, Object token);
public final void removeCallbacksAndMessages(Object token);
public final void removeMessages(int what);
public final void removeMessages(int what, Object object);

避免內(nèi)存泄露的方法

使用軟/弱引用

在Android應用的開發(fā)中瞻鹏,為了防止內(nèi)存溢出,在處理一些占用內(nèi)存大而且聲明周期較長的對象時候赢赊,可以盡量應用軟引用和弱引用技術乙漓。

盡量避免使用 static 成員變量

如果成員變量被聲明為 static,那我們都知道其生命周期將與整個app進程生命周期一樣释移。
這會導致一系列問題叭披,如果你的app進程設計上是長駐內(nèi)存的,那即使app切到后臺玩讳,這部分內(nèi)存也不會被釋放涩蜘。按照現(xiàn)在手機app內(nèi)存管理機制,占內(nèi)存較大的后臺進程將優(yōu)先回收熏纯,如果此app做過進程互保蓖耄活,那會造成app在后臺頻繁重啟樟澜。當手機安裝了你參與開發(fā)的app以后一夜時間手機被消耗空了電量误窖、流量,你的app不得不被用戶卸載或者靜默秩贰。
這里修復的方法是:
不要在類初始時初始化靜態(tài)成員霹俺。可以考慮lazy初始化毒费。 架構(gòu)設計上要思考是否真的有必要這樣做丙唧,盡量避免。如果架構(gòu)需要這么設計觅玻,那么此對象的生命周期你有責任管理起來想际。

避免重寫 finalize()

1、finalize 方法被執(zhí)行的時間不確定溪厘,不能依賴與它來釋放緊缺的資源胡本。時間不確定的原因是: 虛擬機調(diào)用GC的時間不確定 Finalize daemon線程被調(diào)度到的時間不確定
2、finalize 方法只會被執(zhí)行一次畸悬,即使對象被復活打瘪,如果已經(jīng)執(zhí)行過了 finalize 方法,再次被 GC 時也不會再執(zhí)行了,原因是:含有 finalize 方法的 object 是在 new 的時候由虛擬機生成了一個 finalize reference 在來引用到該Object的闺骚,而在 finalize 方法執(zhí)行的時候彩扔,該 object 所對應的 finalize Reference 會被釋放掉,即使在這個時候把該 object 復活(即用強引用引用住該 object )僻爽,再第二次被 GC 的時候由于沒有了 finalize reference 與之對應虫碉,所以 finalize 方法不會再執(zhí)行。
3胸梆、含有Finalize方法的object需要至少經(jīng)過兩輪GC才有可能被釋放敦捧。

參考

https://github.com/GeniusVJR/LearningNotes/blob/master/Part1/Android/Android%E5%86%85%E5%AD%98%E6%B3%84%E6%BC%8F%E6%80%BB%E7%BB%93.md

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市碰镜,隨后出現(xiàn)的幾起案子兢卵,更是在濱河造成了極大的恐慌,老刑警劉巖绪颖,帶你破解...
    沈念sama閱讀 206,839評論 6 482
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件秽荤,死亡現(xiàn)場離奇詭異,居然都是意外死亡柠横,警方通過查閱死者的電腦和手機窃款,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 88,543評論 2 382
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來牍氛,“玉大人晨继,你說我怎么就攤上這事“峥。” “怎么了紊扬?”我有些...
    開封第一講書人閱讀 153,116評論 0 344
  • 文/不壞的土叔 我叫張陵,是天一觀的道長唉擂。 經(jīng)常有香客問我珠月,道長,這世上最難降的妖魔是什么楔敌? 我笑而不...
    開封第一講書人閱讀 55,371評論 1 279
  • 正文 為了忘掉前任,我火速辦了婚禮驻谆,結(jié)果婚禮上卵凑,老公的妹妹穿的比我還像新娘。我一直安慰自己胜臊,他們只是感情好勺卢,可當我...
    茶點故事閱讀 64,384評論 5 374
  • 文/花漫 我一把揭開白布漏益。 她就那樣靜靜地躺著约急,像睡著了一般书妻。 火紅的嫁衣襯著肌膚如雪吱韭。 梳的紋絲不亂的頭發(fā)上凡傅,一...
    開封第一講書人閱讀 49,111評論 1 285
  • 那天,我揣著相機與錄音朝聋,去河邊找鬼沉馆。 笑死,一個胖子當著我的面吹牛抚吠,可吹牛的內(nèi)容都是我干的常潮。 我是一名探鬼主播,決...
    沈念sama閱讀 38,416評論 3 400
  • 文/蒼蘭香墨 我猛地睜開眼楷力,長吁一口氣:“原來是場噩夢啊……” “哼喊式!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起萧朝,我...
    開封第一講書人閱讀 37,053評論 0 259
  • 序言:老撾萬榮一對情侶失蹤岔留,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后检柬,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體献联,經(jīng)...
    沈念sama閱讀 43,558評論 1 300
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 36,007評論 2 325
  • 正文 我和宋清朗相戀三年厕吉,在試婚紗的時候發(fā)現(xiàn)自己被綠了酱固。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 38,117評論 1 334
  • 序言:一個原本活蹦亂跳的男人離奇死亡头朱,死狀恐怖运悲,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情项钮,我是刑警寧澤班眯,帶...
    沈念sama閱讀 33,756評論 4 324
  • 正文 年R本政府宣布,位于F島的核電站烁巫,受9級特大地震影響署隘,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜亚隙,卻給世界環(huán)境...
    茶點故事閱讀 39,324評論 3 307
  • 文/蒙蒙 一磁餐、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧阿弃,春花似錦诊霹、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,315評論 0 19
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至入愧,卻和暖如春鄙漏,著一層夾襖步出監(jiān)牢的瞬間嗤谚,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 31,539評論 1 262
  • 我被黑心中介騙來泰國打工怔蚌, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留巩步,地道東北人。 一個月前我還...
    沈念sama閱讀 45,578評論 2 355
  • 正文 我出身青樓媚创,卻偏偏與公主長得像渗钉,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子钞钙,可洞房花燭夜當晚...
    茶點故事閱讀 42,877評論 2 345

推薦閱讀更多精彩內(nèi)容

  • 寫在前面 前段時間寫了一篇MVP初嘗試鳄橘,由于當時只是剛接觸,只是簡單的實現(xiàn)芒炼,還有很多問題沒想明白瘫怜。關于內(nèi)存泄露這事...
    xiasuhuei321閱讀 3,497評論 9 38
  • 個人主頁:http://shiyiliang.cn 什么是內(nèi)存泄露 通俗的講:不在使用的對象,其內(nèi)存不能回收本刽,導致...
    稻田上的稻草人閱讀 382評論 0 4
  • 前言 對于內(nèi)存泄漏鲸湃,我想大家在開發(fā)中肯定都遇到過,只不過內(nèi)存泄漏對我們來說并不是可見的子寓,因為它是在堆中活動暗挑,而要想...
    EsonJack閱讀 867評論 1 3
  • 我的博客:http://xuyushi.github.io原文地址 [TOC] 內(nèi)存泄露 內(nèi)存泄露的定義:當某些對...
    接地氣的二呆閱讀 1,287評論 2 23
  • 參考內(nèi)存泄露從入門到精通三部曲之基礎知識篇Android 內(nèi)存泄漏總結(jié)Android內(nèi)存泄漏研究Android內(nèi)存...
    合肥黑閱讀 430評論 0 3