內存泄漏的學習

內存泄漏介紹

內存泄漏是針對與堆內存而言的。

Java的內存管理就是對象的分配和釋放。內存分配是由程序進行的,內存的釋放是由GC完成。GC只能回收那些無用,且不被其他對象引用的對象們佔用的空間。
從Main方法開始延伸,所有可以到達的對象都是有效對象,組成對象集合,這些不能被回收。其他的孤立對象則是GC回收的目標。

    {
        Object o = new Object();
    }

局部變量的生命週期是在大括號結束。這時候,棧內的o被銷燬,則 new Object() 這個對象不在被引用,成爲孤立對象,會被GC回收。
所以,內存泄漏的根本原因是:堆內存中的長生命週期的對象持有短生命週期對象的強/軟引用,儘管短生命週期對象已經不再需要,但因爲長生命週期對象持有對他的引用而導致他不能被回收。

常見的內存泄漏

  • 集合

    集合如果只有添加方法,沒有刪除方法,內存會被大量佔用。如果集合是全局的,會造成內存只增不減。
    
  • 單例

    單例對象在被初始化後會在JVM整個生命週期中存在,如果單例持有了外部對象,會造成外部對象不能被回收,致使內存泄漏。

public class Test{
    private static Test test;
    private Context context;

    private Test(Context context){
        this.context = context;
    }

    public static Test getInstance(Context context){
        if(test == null){
            test = new Test(context);
        }

        return test;
    }
}

如果傳入的context是一個activity,會使activity一直存在於內存中,不能被釋放。

  • Android中的各種組件

    BroadCastReceiver,ContentObserver,Cursor等,在Regist後,記得在結束時調用unRegister或close(),並且,不要將Activity作爲成員變量使用,例如上面單例的使用。如果一定需要,則記得使用 WeakReference。

  • 非靜態內部類

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(mResource == null){
            mResource = new TestResource();
        }
        //...
    }

    class TestResource {
    //...
    }
}

外部類的靜態成員mResource的生命週期是整個程序的運行時間,而內部類的對象默認持有對外部類對象的一個引用,所以會導致Activity無法得到釋放。
可以將內部類定義爲靜態內部類,則可以將內部類和外部類的對象的關係切斷,只與類有關。

  • 線程造成的內存泄漏

    因爲線程的生命週期不可控,如果線程保存了對外部對象的引用,會造成外部對象的不可釋放。

  • Handler造成的內存泄漏

    如果handler發送的message沒有被處理,那麼MessageQueue將一直保留對handler,message的引用,而且handler與Activity的生命週期並不一致,可能會導致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();
    }
}

以上代碼,發送了延時的消息,這消息並未被處理時,該頁面退出會導致該頁面無法被回收。

注意事項

  • 對activity的引用要考慮activity的生命週期,不過不能控制在其生命週期內,可以考慮用getApplicationContext(),避免activity長時間被佔用。
  • 儘量不要在靜態內部類使用非靜態的成員變量,必須要用,也要在適當的時候將其置爲空;也可以在靜態內部類中使用弱引用來引用外部成員變量。
  • Handler持有的引用對象最好爲弱引用,資源釋放的時候可以清空handler中的消息。
  • 線程處理耗時操作,在頁面返回時即使取消。
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章