线程对象在完成后不进行垃圾回收

时间:2012-07-24 22:47:16

标签: android debugging memory-leaks garbage-collection ddms

我注意到我的应用程序正在泄漏内存。这可以在DDMS和我看到 设法获得OutOfMemoryError。

我找到了泄漏源。其中一个活动有一个在后台运行的线程。该线程在onDestroy()中停止。它完成了运行,因为它可以在DDMS中看到。

现在,如果线程被启动,则发生泄漏,Activity被销毁后不会被垃圾收集,因为它被线程引用。 如果线程根本没有启动,一切都还可以。

以下是证明这一点的简单示例:

public class MainActivity extends Activity {

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

    volatile boolean finished = false;
    byte[] memoryEater = new byte[4 * 1024 * 1024];

    Thread thread = new Thread(new Runnable() {

        @Override
        public void run() {
            while (!finished) {
                try {
                    Thread.sleep(100);
                } catch (InterruptedException e) {
                    // TODO Auto-generated catch block
                    e.printStackTrace();
                }
            }
            Log.d(getClass().getName(), "Thread finished");
        }
    });

    @Override
    protected void onDestroy() {
        super.onDestroy();
        finished = true;
    }

    public void startActivity(View view) {
        startActivity(new Intent(this, MainActivity.class));
    }

    public void startThread(View view) {
        thread.start();
    }
}

添加一个用于启动新活动的按钮和一个用于启动线程的按钮。开始新的活动。返回后,只有在线程尚未启动时才会清理内存。

这种行为的原因是什么?

4 个答案:

答案 0 :(得分:13)

我刚刚发现了同样的问题。

托马斯,你走在正确的轨道上。 DDMS中没有错误,程序中没有内存泄漏。

真正的问题是你是在DEBUG模式下运行你的程序(在Eclipse下)。不知何故,当Android在DEBUG模式下运行时,即使在退出run()方法之后,线程也不会被垃圾回收。我想这可能是Android需要坚持使用线程才能使某些调试功能正常工作。

但是如果你在RUN模式下运行应用程序(仍然在Eclipse下),就会发生Thread垃圾收集。线程将被完全释放,您的活动将被完全释放。

答案 1 :(得分:4)

我一直在调查,我发现的确是令人惊讶的。似乎没有真正的内存泄漏。仅当app在DDMS中处于调试模式时才会发生。

DDMS似乎以某种方式保留对那些成品胎面的参考,从而阻止它们被GC编辑。当我断开手机并再次连接时,我可以看到所有“泄露”的资源都已被释放。

它看起来像是DDMS中的一个错误。

答案 2 :(得分:2)

线程使用的匿名runnable类将引用该活动('this')。由于活动引用了线程,并且线程中的runnable引用了该活动,因此GC将永远不会收集它们中的任何一个。

尝试做更像这样的事情:

private static RunnableClass implements Runnable
{
    @Override
    public void run() {
        while (!finished) {
            try {
                Thread.sleep(100);
            } catch (InterruptedException e) {
                // TODO Auto-generated catch block
                e.printStackTrace();
            }
        }
        Log.d(getClass().getName(), "Thread finished");
    }
});

Thread thread = new Thread(new RunnableClass());

答案 3 :(得分:0)

当按下“后退”或发送任何其他意图将其从堆栈顶部移除时,不会销毁活动。 在您的Android操作系统内存不足之前,我认为您的覆盖onDestroy()方法不会被调用。 从documentation可以提取:

  

如以下关于活动生命周期的部分所述,   Android系统为您管理活动的生命,所以您可以   不需要完成自己的活动。调用这些方法可以   对预期的用户体验产生不利影响,只应使用   当你绝对不希望用户返回此实例时   活动。

通常,每个Activity实例在初始创建后都会在内存中存放,并且会经历onStart()...onStop()循环而不会被破坏。实施onStop()并在其中调用finish() MainActivity,并释放线程并进行垃圾收集。

更新:上述陈述不属实。根据问题中提供的代码,没有理由不对GC进行GC编辑。