如何强制因 Android 上的内存泄漏导致崩溃?

How to force a crash from memory leak on Android?

所以,我经常听到"holding on to a static activity or view, especially inside an AsyncTask that is long running will cause a memory leak and crash your app"。

但是,我未能在 Android 模拟器中实际证明这一点。

我做错了什么?

public class MainActivity extends AppCompatActivity {

static TextView label;
static List<Activity> sHolder = new ArrayList<>();

@Override
protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.activity_main);

    ViewGroup vg = findViewById(R.id.blah);
    for (int i=0; i<1000000; i++) {
        ImageView im = new ImageView(this);
        im.setImageDrawable(getApplicationContext().getDrawable(R.drawable.kitten_original));
        vg.addView(im);

        new MyTask(this).execute();
    }

    sHolder.add(this);
}

@Override
protected void onStart() {
    super.onStart();
}

@Override
protected void onStop() {
    super.onStop();
}

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

class MyTask extends AsyncTask<Void, Void, Void> {

    Activity activity;
    public MyTask(Activity activity) {
        this.activity = activity;
    }
    @Override
    protected Void doInBackground(Void... voids) {
        try {
            Thread.sleep(10000000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        return null;
    }
}

不是每次崩溃,我的电脑只是开始有点滞后,它一直看到大约 100 MB 的垃圾收集发生。

我怎样才能真正强制应用程序崩溃? (请不要问我为什么要强制应用程序崩溃,我正在做一些极限测试)

I often hear that "holding on to a static activity or view, especially inside an AsyncTask that is long running will cause a memory leak and crash your app".

这是一个简化的解释。

However, I've been unsuccessfully able to actually prove that in an Android Emulator.

我怀疑这段代码不是运行ning。你应该在循环经过几百次后崩溃,因为你可以有多少排队 AsyncTasks,而且这个限制与内存消耗无关。

忽略百万 AsyncTasks 和百万 ImageViews,你通过 sHolder 泄露了 activity,就像我在这个示例中泄露 activity 通过在其布局中持有对 Buttonstatic 引用:

/***
 Copyright (c) 2015 CommonsWare, LLC
 Licensed under the Apache License, Version 2.0 (the "License"); you may not
 use this file except in compliance with the License. You may obtain    a copy
 of the License at http://www.apache.org/licenses/LICENSE-2.0. Unless required
 by applicable law or agreed to in writing, software distributed under the
 License is distributed on an "AS IS" BASIS,    WITHOUT WARRANTIES OR CONDITIONS
 OF ANY KIND, either express or implied. See the License for the specific
 language governing permissions and limitations under the License.

 Covered in detail in the book _The Busy Coder's Guide to Android Development_
 https://commonsware.com/Android
 */

package com.commonsware.android.button;

import android.app.Activity;
import android.os.Bundle;
import android.widget.Button;

public class ButtonDemoActivity extends Activity {
  private static Button pleaseDoNotDoThis;

  @Override
  public void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.main);

    pleaseDoNotDoThis=(Button)findViewById(R.id.button1);
  }
}

您可以使用 LeakCanary、Android Studio 的堆分析器等来证明发生泄漏。但是,要证明该泄漏,您需要 运行 应用程序,然后按返回,然后查看你销毁的 activity 没有被垃圾回收。或者,您需要 运行 应用程序,旋转屏幕(或进行任何其他类型的配置更改),然后看到您现在有两个 activity 实例,一个被破坏和泄漏的实例加上当前的一个。如果你只是 运行 应用程序什么都不做,你就没有泄露 activity — 虽然你有自己的静态引用,但 Android 也是如此,因为 activity在前台,用户可以看到。

泄漏本身不会导致崩溃。这只是意味着您正在占用无法用于其他用途的堆 space。最终,您将在某些分配上获得 OutOfMemoryError。如果你只想用 OutOfMemoryError 崩溃,请尝试分配一些大的 byte[](比如 1GB)。

如果您特别想测试由泄露活动触发的 OutOfMemoryError,您需要:

  • 让 activity 分配大量堆 space(例如 1MB byte[]),

  • 像您的 sHolder 列表一样做一些事情,然后

  • 旋转屏幕很多,或者让自己处于这样一种情况,即会创建和销毁 activity 的大量实例,但不会被垃圾收集,这要归功于你的 sHolder