哪些Android工具和方法最适合发现内存/资源泄漏?[关闭]


152

我已经开发了一个Android应用程序,而我正在进行电话应用程序开发,一切似乎都很好,并且您想宣告胜利并出货,但是您知道必须存在一些内存和资源泄漏在那里; Android上只有16mb的堆,而且显然很容易在Android应用中泄漏。

我一直在环顾四周,到目前为止,我们只能挖掘有关“ hprof”和“ traceview”的信息,而且都没有得到很多好评。

您遇到或开发了哪些工具或方法,并希望在OS项目中共享这些工具或方法?


3
我是否可以投票要求Р̀СТȢѸ́ФХѾЦЧШЩЪЫЬѢѤЮѦѪѨѬѠѺѮѰѲѴ将他的名字改成易于理解的名称。
JPM 2015年

1
如果我们从问题标题中删除“ Android Tools”一词,可以重新打开该问题吗?在这里找到的答案对于解决我的内存泄漏问题非常有用
k3b 2015年

好的,我让用户不断在活动之间切换,这意味着他们可能在20秒内切换了15个活动。这可能是内存不足错误的原因吗?我应该怎么做才能解决?谢谢!
Ruchir Baronia

1
由于问题已经解决,因此无法提供此答复,但是我建议您看一下Leak Canary。只需使用您的应用程序,打开和关闭活动,然后由图书馆来完成即可。它甚至可以告诉您泄漏发生的位置。泄漏发生后,请给泄漏分析仪一些时间来进行工作-通常大约需要2分钟或更长时间才能找到泄漏的根源。之后,它将在应用程序中整齐地呈现给您。无需额外的工具!
ubuntudroid

Answers:


90

我发现开发Android应用程序时最常见的错误之一是“ java.lang.OutOfMemoryError:位图大小超出VM预算”错误。我在更改方向后频繁使用大量位图在活动中发现此错误:活动被销毁,再次创建,布局从XML“膨胀”,消耗了位图可用的VM内存。

垃圾回收器未正确释放先前活动布局上的位图,因为它们已经交叉引用了其活动。经过多次实验,我发现了一个很好的解决方案。

首先,在XML布局的父视图上设置“ id”属性:

<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
     android:layout_width="fill_parent"
     android:layout_height="fill_parent"
     android:id="@+id/RootView"
     >
     ...

然后,在Activity的onDestroy()方法上,调用unbindDrawables()方法,将引用传递给父View,然后执行System.gc()

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

    unbindDrawables(findViewById(R.id.RootView));
    System.gc();
}


private void unbindDrawables(View view) {

    if (view.getBackground() != null) {
        view.getBackground().setCallback(null);
    }

    if (view instanceof ViewGroup) {
        for (int i = 0; i < ((ViewGroup) view).getChildCount(); i++) {
            unbindDrawables(((ViewGroup) view).getChildAt(i));
        }

        ((ViewGroup) view).removeAllViews();
    }
}

此unbindDrawables()方法以递归方式探索视图树,并:

  1. 删除所有背景可绘制对象上的回调
  2. 在每个视图组上删除子级

3
一个常见问题的好解决方案。
While-E

9
这不适用于AdapterView的子类(ListView,GridView等)。
Arjun

@Arjun是..它不适用于AdapterView子类。为此,您需要处理异常。休息它工作正常。这就是我在代码中使用的方法,并且效果很好。希望这可以帮助。
hp.android

@ hp.android通过“例外处理”,您有一个示例的例子吗?我问是因为我的页面中有图像,PageAdapter并且我正在处理此错误:(
Jacksonkr,2012年

4
@Jackson只是更改条件:如果(查看ViewGroup的实例&&!(查看AdapterView的实例)),这将消除您为适配器获得的异常
hp.android 2012年


28

主要面向未来的Google旅行者:

不幸的是,大多数Java工具都不适合该任务,因为它们仅分析JVM-Heap。但是,每个Android应用程序也都有一个本机堆,该堆也必须符合〜16 MB的限制。例如,它通常用于位图数据。因此,即使您使用大量可绘制对象,即使您的JVM-Heap的内存约为3 MB,您也可以很容易地遇到内存不足错误。


6
开始的Android 3.0(蜂窝)可绘制对象存储在堆中
Gu1234 2011年

@Timo那么您将用什么来检测本机堆中的泄漏?
sydd 2011年

1
测试,很多测试。问题是由于内存共享和其他优化技术,您甚至无法真正确定应用程序正在使用多少内存。您可以使用常规的shell命令获取内存读数,但是这些估算值非常非常粗略。
Timo Ohr,

20

从@ hp.android答案运作良好,如果你只是用位图背景的工作,但是,在我的情况,我有一个BaseAdapter提供一组ImageView为A S GridView。我unbindDrawables()按照建议修改了方法,使条件为:

if (view instanceof ViewGroup && !(view instanceof AdapterView)) {
  ...
}

但是问题在于递归方法永远不会处理的子级AdapterView。为了解决这个问题,我改为执行以下操作:

if (view instanceof ViewGroup) {
  ViewGroup viewGroup = (ViewGroup) view;
  for (int i = 0; i < viewGroup.getChildCount(); i++)
    unbindDrawables(viewGroup.getChildAt(i));

  if (!(view instanceof AdapterView))
    viewGroup.removeAllViews();
}

这样的孩子 AdapterView仍处理的子项-该方法只是不尝试删除所有子项(不受支持)。

但这不能完全解决问题,因为ImageViews管理的位图不是背景。因此,我添加了以下内容。这不是理想的方法,但可以:

if (view instanceof ImageView) {
  ImageView imageView = (ImageView) view;
  imageView.setImageBitmap(null);
}

总体而言,该unbindDrawables()方法是:

private void unbindDrawables(View view) {
  if (view.getBackground() != null)
    view.getBackground().setCallback(null);

  if (view instanceof ImageView) {
    ImageView imageView = (ImageView) view;
    imageView.setImageBitmap(null);
  } else if (view instanceof ViewGroup) {
    ViewGroup viewGroup = (ViewGroup) view;
    for (int i = 0; i < viewGroup.getChildCount(); i++)
    unbindDrawables(viewGroup.getChildAt(i));

    if (!(view instanceof AdapterView))
      viewGroup.removeAllViews();
  }
}

我希望有一种更原则的方法来释放这些资源。




1

好吧,这些是与Android使用的独特格式挂钩的工具。我认为您可能不满意的是所使用的基础测试代码框架。

您是否尝试过使用Android Mock Framework模拟测试代码区域?


1
不用那么多,测试这种性质就不是问题,而是记录应用程序运行时实际发生的事情,我真正需要的是资源/内存泄漏概要分析工具
jottos
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.