在我的应用程序中寻找内存泄漏时,我追查了一个我无法理解的行为。我分配了一个大的内存块,但它不会被垃圾收集而导致OOM,除非我在onDestroy中显式地为null引用。
在这个例子中,我有两个几乎完全相同的活动,可以在彼此之间切换。两者都有一个按钮。按下按钮MainActivity通过调用finish()启动OOMActivity和OOMActivity返回。按几次按钮后,Android会抛出OOMException。
如果我将onDestroy添加到OOMActivity并显式null对内存块的引用,我可以在日志中看到内存被正确释放。
为什么没有归零会自动释放内存?
MainActivity:
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);
}
}
}
OOMActivity:
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();
}
}
}
答案 0 :(得分:1)
活动破坏并不意味着类破坏,只是因为你没有看到类并不意味着操作系统(在这种情况下是Android)丢失了对它的所有引用并结束它。这就是为什么即使在他们指定清除任何句柄和对象的文档中,您也不再需要防止内存泄漏。欢呼声。
答案 1 :(得分:1)
一些事情:
1)您无法仅通过观看GC日志来判断您的活动是否被泄露;每个JVM实现都可以自由选择何时进行垃圾收集对象,即使没有引用它们。请注意,在抛出OOM错误之前需要进行垃圾收集......但是如果它有足够的可用内存,它可以选择将10个活动保留在内存中,然后一次性收集它们。
2)可能有内部Android结构将对Activity的引用保留的时间超过Activity的生命周期......并且开发人员无法控制它。出于这个原因,建议Activity不引用任何大量数据(或者如果它确实如此,它应该在onDestroy中显式释放这些引用(如果你想要更积极的话,甚至在onPause中)。
3)一些JVM在运行时进行优化,这样从未写入或访问过的一块内存永远不会实际分配到物理内存中。也许是因为这个原因,您的测试在较新版本的Android上无效。为了解决这个问题,你可以添加一个循环,将数组中的某些值设置为随机值,然后在代码中的其他位置添加另一个循环来读取它们;这样就迫使JVM分配内存。