Android 理解堆大小

Posted

技术标签:

【中文标题】Android 理解堆大小【英文标题】:Android Understanding Heap Sizes 【发布时间】:2012-05-27 12:24:03 【问题描述】:

我对 android 开发还很陌生,我似乎无法理解 Java Out of Memory 异常。我知道这意味着我的应用程序已经超出了虚拟机预算,但是在谷歌搜索了很多次之后,我似乎仍然没有掌握这个概念。我担心我的应用程序使用了太多内存,因为我每个屏幕有六个按钮选择器,每个选择器有两个位图,根据属性选项卡,每个选择器大约 20 kb。在我的根 G2x 上,我将 VM 预算设置为 12mb,重新启动手机并运行我的应用程序,没有任何问题。我在每个 onDestroy() 上取消绑定可绘制对象,并暗示 GC 也在这里运行。在模拟器中使用该应用程序一段时间后,我在 DDMS 屏幕上单击“Cause GC”,结果为 ID=1,堆大小 6.133 MB,已分配 2.895MB,可用 3.238 MB,使用百分比 47.20,# 对象 52,623。

这是我不明白发生了什么的地方,我的模拟器设置为 24MB 的 VM。那个号码在哪里?我遇到的实际问题是,如果我将模拟器设置为 16MB 的 VM,我的应用程序在第二个活动中崩溃并出现内存不足异常。为什么它不会在我的 VM 设置为 12 MB 的手机上或在我的旧 HTC Magic 手机上与 12 MB 的 VM 库存上崩溃?另外,我的应用程序是否占用了太多内存?我不知道那些 DDMS 数字好不好。

至于我的代码,我在 XML 布局中指定了每个图像。除了向它们添加侦听器之外,我不会以编程方式对它们做任何事情。我在这里找到了这段代码,并将其添加到我拥有的每个活动中......

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

    unbindDrawables(findViewById(R.id.myRootLayout));
    System.gc();


private void unbindDrawables(View view) 
    if (view.getBackground() != null) 
        view.getBackground().setCallback(null);
    
    if (view instanceof ViewGroup && !(view instanceof AdapterView)) 
        for (int i = 0; i < ((ViewGroup) view).getChildCount(); i++) 
            unbindDrawables(((ViewGroup) view).getChildAt(i));
        
        ((ViewGroup) view).removeAllViews();
    

否则我所做的只是将onClickListeners 添加到具有PNG 背景的按钮中。我想学习如何以编程方式指定按钮背景,但我需要选择器功能,如焦点、按下、非焦点但按下等,以使按钮背景根据用户交互而变化。我已经查看了有关此的文档,但它似乎势不可挡,这就是为什么我认为我将从管理堆的基础知识开始,然后逐步在代码中指定选择器。这可能没有意义,但是应用程序是否可以分配“健康”的内存分配量而不会接近内存不足异常?例如,如果一个应用程序分配了 6MB 应该没问题,但 8MB 会推送它,内存分配是否有类似的界限?

【问题讨论】:

处理 OutOfMemoryError 时要考虑的另一件事是内存泄漏。查看Wrangling Dalvik: Memory Management in Android 【参考方案1】:

当您在模拟器/设备上设置 VM 预算时,您所做的是告诉堆允许的最大大小。在运行时,随着 Dalvik VM 向操作系统请求系统内存,堆的大小会动态增长。 Dalvik VM 通常从分配一个相对较小的堆开始。然后在每次 GC 运行后,它会检查有多少可用的堆内存。如果空闲堆与总堆的比例太小,Dalvik VM 将向堆中添加更多内存(直到配置的最大堆大小)。

话虽如此,您在 DDMS 屏幕上看不到“24 mb”的原因是堆尚未增长到最大大小。这使 Android 可以充分利用手持设备上已经很少可用的内存。

至于为什么您的应用程序在模拟器而不是您的手机上崩溃,这看起来很奇怪(您确定数字是正确的吗?)。但是,您应该记住,内存是动态管理的,并且总内存利用率是根据许多外部因素(执行垃圾收集的速度/频率等)确定的。

最后,由于我上面提到的原因,根据您上面提供的单行信息,很难确定您的应用程序管理内存的能力如何。我们真的需要看看你的一些代码。不过,OutOfMemoryErrors 绝对值得担心,所以我肯定会调查您的应用程序的内存使用情况。您可能会考虑的一件事是在运行时使用BitmapFactory 类调用inSampleSize 对位图图像进行采样。这有助于减少加载可绘制位图所需的内存量。要么这样,要么您可以降低可绘制对象的分辨率(尽管每个 20 kb 对我来说听起来不错)。

【讨论】:

非常感谢!我从中学到了很多。我在问题的底部添加了一些信息 重新解析注释“每个 20 kb 听起来不错” - AFAIK,文件大小 无关紧要(对于压缩得更好的图像,它会更小), DIMENSIONS 是什么很重要;在内存中,对于全彩色,每个像素需要 4B,对吧?因此,在加载图像时调整图像大小很重要 [android docs - Load a Scaled Down Version into Memory],基于当前设备的实际需要大小。【参考方案2】:

即使您的应用程序没有达到“24mb”(因设备而异)堆限制,您仍然可能会发生崩溃,因为 Android 需要一段时间来为您的应用程序增加堆空间。

在我的情况下,我在很短的时间内创建并转储了几张图像。

我经常收到OutOfMemoryError

Android 似乎不够快,无法为我的应用增加堆空间。

我相信我通过使用清单文件中的largeHeap 设置解决了这个问题。 启用该设置后,Android 每次增加堆时都会留下更多可用内存,从而最大限度地减少达到当前限制的机会。

我没有使用 24mb 的限制,但是这个 largeHeap conf 非常方便。

你只需要在AndroidManifest.xml的应用标签上设置largeHeap="true"

<application
    android:icon="@drawable/ic_launcher"
    android:label="@string/app_name"
    android:largeHeap="true"
    android:theme="@style/AppTheme" >

不过,请确保在处理图像时小心谨慎,就像 @Alex Lockwood 建议的那样。

【讨论】:

谨慎使用 largeHeap="true"。因为使用它可能会对您的应用程序性能产生不利影响。因为您正在告诉系统增加最大堆限制。发生这种情况时,垃圾收集将需要更多时间。如果查看日志,您可以看到 GC Pause 时间会更长。理想情况下,它应该在 2-5ms 之间。在这种情况下,它甚至可以变化到 30-45ms。因此,不要仅仅因为内存不足而将大堆属性设置为 true。使用它作为最后一步。否则它将是一个性能打击。 确实如此。就我而言,我正在动态处理大量高质量图像,并且在使用 largeHeap 选项之前,我已经实现了以下代码: - 加载采样的图像 - 调整图像大小以完美匹配 - 从内存中清除未使用的图像 - 将图像缓存在“磁盘”我仍然遇到只能使用 largeHeap 选项解决的问题。不过,只有在尝试了其他所有方法后才使用它,就像 @SanalVarghese 建议的那样。 除此之外,我的应用程序内存一直存在很多问题,它的大小大大增加。此时它可能无法有效地设置在一起,但它应该能够不超过 heep 限制。但是,它永远不会释放很多资源。由于某种原因,它在应用 largeHeap 选项后做得很好。非常感谢。

以上是关于Android 理解堆大小的主要内容,如果未能解决你的问题,请参考以下文章

Android:每个意图/活动的堆大小是多少?

Android中JVM的堆大小[重复]

如何检测 android 应用程序的堆大小使用情况

如何在 Android 2.3 (Gingerbread) 上增加堆大小?

Android:如何在运行时增加堆大小?

不同Android设备的堆大小[重复]