1. 程式人生 > >Java中內部類的記憶體洩露問題

Java中內部類的記憶體洩露問題

package com.example.temptemp;

import android.app.Activity;
import android.os.Bundle;
import android.util.Log;
import android.widget.TextView;

public class SecondActivity extends Activity {
    byte[] bigData = new byte[10 * 1024 * 1024];

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        ((TextView) findViewById(R.id.txt)).setText("SecondActivity");
    }

    @Override
    protected void onDestroy() {
        super.onDestroy();
        // test();
        test2();
    }

    private void test() {
        new Thread() {

            @Override
            public void run() {
                super.run();
                Log.e("SS", "start");
                try {
                    Thread.sleep(15000);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                Log.e("SS", "end");
            }
        }.start();
    }


    private static void test2() {
        new Thread() {

            @Override
            public void run() {
                super.run();
                Log.e("SS", "start");
                try {
                    Thread.sleep(15000);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                Log.e("SS", "end");
            }
        }.start();
    }

}

這是一個簡單的Activity,裡面有兩個簡單的方法,test()和test2(),test()和test2()的唯一區別就是一個是靜態的,一個非靜態的。

同時這個類有個成員變數bigData,主要是分配10M的記憶體,便於檢視記憶體的變化。

測試方法:

只調用test方法:當Activity銷燬後,發現只有等執行緒結束後,該Activity才能被回收;

只調用test2方法:當Activity銷燬後,發現該Activity能立即被回收。

原因就是Java中內部類(匿名內部類也一樣)會有宿主類的強引用。也就是this變數的來源。是編譯預設行為。用javap檢視class檔案可以看出。

Compiled from "SecondActivity.java"
class com.example.temptemp.SecondActivity$1 extends java.lang.Thread {
  final com.example.temptemp.SecondActivity this$0;

  com.example.temptemp.SecondActivity$1(com.example.temptemp.SecondActivity);
    Code:
       0: aload_0
       1: aload_1
       2: putfield      #10                 // Field this$0:Lcom/example/temptemp/SecondActivity;
       5: aload_0
       6: invokespecial #12                 // Method java/lang/Thread."<init>":()V
       9: return

  public void run();
    Code:
       0: aload_0
       1: invokespecial #20                 // Method java/lang/Thread.run:()V
       4: ldc           #22                 // String SS
       6: ldc           #24                 // String start
       8: invokestatic  #26                 // Method android/util/Log.e:(Ljava/lang/String;Ljava/lang/String;)I
      11: pop
      12: ldc2_w        #32                 // long 15000l
      15: invokestatic  #34                 // Method java/lang/Thread.sleep:(J)V
      18: goto          26
      21: astore_1
      22: aload_1
      23: invokevirtual #38                 // Method java/lang/InterruptedException.printStackTrace:()V
      26: ldc           #22                 // String SS
      28: ldc           #43                 // String end
      30: invokestatic  #26                 // Method android/util/Log.e:(Ljava/lang/String;Ljava/lang/String;)I
      33: pop
      34: return
    Exception table:
       from    to  target type
          12    18    21   Class java/lang/InterruptedException
}

注意看this$0,就是對宿主類的引用。

結論:在內部類中這種記憶體洩露一般很容易被忽略,比如經常會在Activity onDestroy中進行一些回收,或者同步工作,這時也應當避免做耗時的操作,就算耗時操作用匿名內部類的Thread來做,也同樣可能造成洩露。