16

在寻找我的应用程序中的内存泄漏时,我追查了一个我无法理解的行为。我分配了一个大的内存块,但它不会被垃圾收集,从而导致 OOM,除非我在 onDestroy 中显式地将引用设为空。

在这个例子中,我有两个几乎相同的活动,它们在彼此之间切换。两者都有一个按钮。在按下按钮 MainActivity 启动 OOMActivity 并且 OOMActivity 通过调用 finish() 返回。按下按钮几次后,Android 会抛出 OOMException。

如果我将 onDestroy 添加到 OOMActivity 并显式 null 对内存块的引用,我可以在日志中看到内存已正确释放。

为什么在没有清零的情况下内存不会自动释放?

主要活动:

package com.example.oom;

import android.app.Activity;
import android.content.Intent;
import android.os.Bundle;
import android.view.View;
import android.view.View.OnClickListener;
import android.widget.Button;

public class MainActivity extends Activity implements OnClickListener {

    private int buttonId;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        System.gc();
        Button OOMButton = new Button(this);
        OOMButton.setText("OOM");
        buttonId = OOMButton.getId();

        setContentView(OOMButton);
        OOMButton.setOnClickListener(this);
    }

    @Override
    public void onClick(View v) {
        if (v.getId() == buttonId) {
            Intent leakIntent = new Intent(this, OOMActivity.class);
            startActivity(leakIntent);
        }
    }

}

OOM活动:

public class OOMActivity extends Activity implements OnClickListener {

    private static final int WASTE_SIZE = 20000000;
    private byte[] waste;
    private int buttonId;

    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);

        Button BackButton = new Button(this);
        BackButton.setText("Back");
        buttonId = BackButton.getId();

        setContentView(BackButton);
        BackButton.setOnClickListener(this);

        waste = new byte[WASTE_SIZE];

    }

    public void onClick(View view) {
        if (view.getId() == buttonId) {
            finish();
        }
    }

}
4

2 回答 2

1

Activity 销毁并不意味着类销毁,仅仅因为您没有看到该类并不意味着操作系统(在本例中为 Android)丢失了对它的所有引用并结束它。这就是为什么即使在他们指定清除任何句柄和对象的文档中,您也不再需要防止内存泄漏。干杯。

于 2013-06-24T16:18:07.707 回答
1

一些东西:

1)你不能仅仅通过查看GC日志来判断你的Activity是否被泄露;每个 JVM 实现都可以自由选择何时对对象进行垃圾收集,即使没有引用它们。请注意,它需要在引发 OOM 错误之前进行垃圾收集......但如果它有足够的可用内存,它可能会选择将 10 个活动保留在内存中,然后一次收集它们。

2) 可能有内部 Android 结构对您的 Activity 的引用保持比 Activity 的生命周期更长的时间......并且开发人员无法控制它。出于这个原因,建议 Activity 不引用任何大量数据(或者如果这样做,它应该在 onDestroy 中显式释放这些引用(如果您想更积极,甚至在 onPause 中)。

3) 一些 JVM 在运行时进行优化,使得一块从未被写入或访问过的内存永远不会真正分配到物理内存中。也许出于这个原因,您的测试在较新版本的 Android 上无效。为了解决这个问题,您可以添加一个循环,将数组中的一些值设置为随机值,然后在读取它们的代码中的其他位置添加另一个循环;这样JVM被迫分配内存。

于 2013-07-22T10:47:16.580 回答