10

我注意到我的应用程序正在泄漏内存。这可以在 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

4 回答 4

13

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

Tomasz,你在正确的轨道上。DDMS 中没有错误,您的程序中也没有内存泄漏。

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

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

于 2012-10-10T06:38:34.593 回答
4

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

DDMS 似乎以某种方式持有对那些完成的胎面的引用,从而防止它们被 GC 编辑。当我断开电话并再次连接时,我可以看到所有“泄漏”的资源都已释放。

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

于 2012-07-27T06:25:40.430 回答
2

线程使用的匿名可运行类将引用活动(“this”)。由于线程被活动引用,并且线程中的可运行对象引用了活动,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());
于 2012-07-24T22:55:25.227 回答
0

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

正如下面关于 Activity 生命周期的部分所讨论的,Android 系统会为您管理 Activity 的生命周期,因此您无需完成自己的 Activity。调用这些方法可能会对预期的用户体验产生不利影响,并且只应在您绝对不希望用户返回此活动实例时使用。

通常,每个 Activity 实例在初始创建后都在内存中,并经过onStart()...onStop()循环而不会被销毁。实现onStop()并调用finish()MainActivity以释放要释放的线程并因此收集垃圾。

更新:上面的陈述不正确。根据问题中提供的代码,没有理由不应该对 Activity 进行 GC-ed。

于 2012-07-24T23:38:28.103 回答