为什么线程在Android上泄漏?

Cam*_*mer 5 java multithreading android garbage-collection

我一直在我们的Android应用程序中注意到,每次我们退出到主屏幕时,我们都会通过ByteArrayOutputStream的数量增加堆大小(泄漏).我能够管理的最好的是添加

this.mByteArrayOutputStream = null;
Run Code Online (Sandbox Code Playgroud)

run()结束时防止堆大小不断增加.如果有人能够启发我,我将非常感激.我写了下面的例子来说明问题.

MainActivity.java

public class MainActivity extends Activity {
    private Controller mController;

    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);        
    }

    @Override
    public boolean onCreateOptionsMenu(Menu menu) {
        getMenuInflater().inflate(R.menu.activity_main, menu);
        return true;
    }

    @Override
    protected void onStart() {
        super.onStart();

        this.mController = new Controller();
        mController.connect();
    }

    @Override
    protected void onStop() {
        super.onStop();

        mController.quit();
    }
}
Run Code Online (Sandbox Code Playgroud)

Controller.java

public class Controller {
    public volatile ReaderThread mThread;

    public Controller() {
        super();
    }

    public void connect() {
        mThread = new ReaderThread("ReaderThread");
        mThread.start();
    }

    public void quit() {
        mThread.quit();
    }

    public static class ReaderThread extends Thread {
        private volatile boolean isProcessing;
        private ByteArrayOutputStream mByteArrayOutputStream;

        public ReaderThread(String threadName) {
            super(threadName);  
        }

        @Override
        public void run() {
            this.isProcessing = true;

            Log.d(getClass().getCanonicalName(), "START");
            this.mByteArrayOutputStream = new ByteArrayOutputStream(2048000);

            int i = 0;
            while (isProcessing) {
                Log.d(getClass().getCanonicalName(), "Iteration: " + i++);
                mByteArrayOutputStream.write(1);
                try {
                    Thread.sleep(1000);
                } catch (InterruptedException e) {
                }
            }

            try {
                mByteArrayOutputStream.reset();
                mByteArrayOutputStream.close();
            } catch (IOException e) {
                e.printStackTrace();
            }

            Log.d(getClass().getCanonicalName(), "STOP");
        }

        public void quit() {
            this.isProcessing = false;
        }
    }
}
Run Code Online (Sandbox Code Playgroud)

acj*_*acj 8

线程对GC免疫,因为它们是垃圾收集根.因此,JVM可能会保留您ReaderThread的内存以及成员变量的分配,从而造成泄漏.

归零了ByteArrayOutputStream,因为你已经注意到,会使它的缓冲数据(但不包括ReaderThread本身)供GC.

编辑:

经过一番调查后,我们了解到Android调试器导致了泄漏:

VM保证调试器知道的任何对象在调试器断开连接之前不会被垃圾收集.在连接调试器时,这会导致对象随时间累积.例如,如果调试器看到正在运行的线程,则即使在线程终止后,关联的Thread对象也不会被垃圾回收.