线程为什么漏在Android? [英] Why do threads leak on Android?

查看:94
本文介绍了线程为什么漏在Android?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我已经注意到在我们的Andr​​oid应用程序,每一次我们退出到主屏幕,我们被ByteArrayOutputStream量增加堆大小(泄漏)。我已经能够管理的最好的是通过添加

  this.mByteArrayOutputStream = NULL;

的run()的底prevent堆的大小不断增加。如果有人可以赐教,我会非常AP preciative。我写了下面的例子说明了这个问题。

MainActivity.java

 公共类MainActivity延伸活动{
    私人控制器mController;    @覆盖
    公共无效的onCreate(捆绑savedInstanceState){
        super.onCreate(savedInstanceState);
        的setContentView(R.layout.activity_main);
    }    @覆盖
    公共布尔onCreateOptionsMenu(菜单菜单){
        。getMenuInflater()膨胀(R.menu.activity_main,菜单);
        返回true;
    }    @覆盖
    保护无效调用onStart(){
        super.onStart();        this.mController =新控制器();
        mController.connect();
    }    @覆盖
    保护无效的onStop(){
        super.onStop();        mController.quit();
    }
}

Controller.java

 公共类控制器{
    公共挥发性ReaderThread mThread;    公共控制器(){
        超();
    }    公共无效连接(){
        mThread =新ReaderThread(ReaderThread);
        mThread.start();
    }    公共无效退出(){
        mThread.quit();
    }    公共静态类ReaderThread继承Thread {
        私人挥发性布尔isProcessing;
        私人ByteArrayOutputStream mByteArrayOutputStream;        公共ReaderThread(字符串threadName){
            超(threadName);
        }        @覆盖
        公共无效的run(){
            this.isProcessing = TRUE;            Log.d(的getClass()另一方面,getCanonicalName(),START);
            this.mByteArrayOutputStream =新ByteArrayOutputStream(2048000);            INT I = 0;
            而(isProcessing){
                Log.d(的getClass()另一方面,getCanonicalName(),迭代:+ I ++);
                mByteArrayOutputStream.write(1);
                尝试{
                    视频下载(1000);
                }赶上(InterruptedException的E){
                }
            }            尝试{
                mByteArrayOutputStream.reset();
                mByteArrayOutputStream.close();
            }赶上(IOException异常五){
                e.printStackTrace();
            }            Log.d(的getClass()另一方面,getCanonicalName(),STOP);
        }        公共无效退出(){
            this.isProcessing = FALSE;
        }
    }
}


解决方案

线程是免疫GC,因为他们是垃圾收集根。因此,它很可能是JVM是保持你的 ReaderThread 在内存中,其分配的成员变量一起,这样就产生了泄漏。

归零了 ByteArrayOutputStream ,如您所知,将使其缓冲的数据(而不是 ReaderThread 本身)供GC。

修改

一些侦探后,我们了解到, Android的调试器是导致感知泄漏


  

该VM保证任何对象调试器是知道是不是垃圾回收直到调试器断开连接后。 可以导致物体随着时间的推移堆积而调试器连接即可。例如,如果调试器看到一个正在运行的线程,相关的Thread对象是不收集垃圾线程终止后,即使


I've been noticing in our Android app that every time we exit to the home screen we increase the heap size (leak) by the amount of the ByteArrayOutputStream. The best I have been able to manage is by adding

this.mByteArrayOutputStream = null;

at the end of run() to prevent the heap size increasing constantly. If anyone could enlighten me I would be very appreciative. I wrote out the following example that illustrates the problem.

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();
    }
}

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;
        }
    }
}

解决方案

Threads are immune to GC because they're garbage collection roots. So, it's likely that the JVM is keeping your ReaderThread in memory, along with its allocations for member variables, thus creating the leak.

Nulling out the ByteArrayOutputStream, as you've noted, would make its buffered data (but not the ReaderThread itself) available for GC.

EDIT:

After some sleuthing, we learned that the Android debugger was causing the perceived leak:

The VM guarantees that any object the debugger is aware of is not garbage collected until after the debugger disconnects. This can result in a buildup of objects over time while the debugger is connected. For example, if the debugger sees a running thread, the associated Thread object is not garbage collected even after the thread terminates.

这篇关于线程为什么漏在Android?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆