說說 記憶體洩漏

weixin_34041003發表於2016-07-19

記憶體洩漏和記憶體溢位是兩個概念。
記憶體洩漏,指的是程式使用過的記憶體“忘記”歸還給系統,造成長時間無謂地佔用。
記憶體溢位,指的是記憶體被大量的使用後,程式再次去申請記憶體時,系統已經不能滿足其要求的情況。
所以,記憶體洩露到一定程度就會導致記憶體溢位。

Java中,記憶體的回收工作是GC來做的。GC也有軟肋:如果持有物件的強引用,GC是無法在記憶體中回收這個物件的。

繞個遠說說幾種引用:

  • 強引用(StrongReference)
    強引用是使用最普遍的引用。如果一個物件具有強引用,那垃圾回收器絕不會回收它。當記憶體空間不足,Java虛擬機器寧願丟擲OutOfMemoryError錯誤,使程式異常終止,也不會靠隨意回收具有強引用的物件來解決記憶體不足的問題。
  • 軟引用(SoftReference)
    如果一個物件只具有軟引用,則記憶體空間足夠,垃圾回收器就不會回收它;如果記憶體空間不足了,就會回收這些物件的記憶體。
  • 弱引用(WeakReference)
    GC掃描過程中,一旦發現了只具有弱引用的物件,不管當前記憶體空間足夠與否,都會回收它的記憶體。
  • 虛引用(PhantomReference)
    如果一個物件僅持有虛引用,那麼它就和沒有任何引用一樣,在任何時候都可能被垃圾回收器回收。

列舉經常造成記憶體洩漏的幾種情況:

  1. static變數
  • static activity
public class MyActivity extends BaseActivity {
    static Activity mActivity = null;
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        mActivity = this;
    }
}

通常,一個activity物件包含了大量的引用,如果賦值給一個靜態變數會使activity脫離系統的管理。被洩漏的activity物件會一直存活在記憶體中,造成記憶體洩漏。

  • static view
public class MyActivity extends BaseActivity {
    static TextView textView = null;
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        textView = new TextView(this);
    }
}
  1. 內部類
    內部類使用起來是比較便捷的,但就是這麼一點福利也要冒著記憶體洩漏的風險,因為非靜態的內部類會持有外部類的一個隱式引用。
  • Thread
public class MyActivity extends BaseActivity {

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        doSth();
    }

    private void doSth() {
        new Thread() {
            @Override
            public void run() {
                while (true) {
                    SystemClock.sleep(1000);
                }
            }
        }.start();
    }
}

假如新執行緒執行的任務非常耗時,十天半個月都做不完。在此期間,外部類物件也不能被回收。TimerTask和AsyncTask也會有同樣的問題。

  • Handler
public class MyActivity extends BaseActivity {

    Handler mHanlder = new Handler();

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        doSth();
    }

    private void doSth() {
        mHanlder.postDelayed(new Runnable() {
            @Override
            public void run() {

            }
        }, Long.MAX_VALUE >> 1);
    }
}

Runnable會持有外部類引用,造成記憶體洩漏。

  1. Sensor Manager
    通過Context.getSystemService(int name)可以獲取系統服務。這些服務工作在各自的程式中,幫助應用處理後臺任務,處理硬體互動。如果需要使用這些服務,可以註冊監聽器,這會導致服務持有了Context的引用,如果在Activity銷燬的時候沒有登出這些監聽器,會導致記憶體洩漏。
public class MyActivity extends BaseActivity implements SensorEventListener {

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        registerListener();

    }

    void registerListener() {
        SensorManager sensorManager = (SensorManager) getSystemService(SENSOR_SERVICE);
        Sensor sensor = sensorManager.getDefaultSensor(Sensor.TYPE_ALL);
        sensorManager.registerListener(this, sensor, SensorManager.SENSOR_DELAY_FASTEST);
    }

    @Override
    public void onSensorChanged(SensorEvent event) {

    }

    @Override
    public void onAccuracyChanged(Sensor sensor, int accuracy) {

    }
}

恩,就醬。

相關文章