2012-05-20 61 views
28

我对Android开发相当陌生,似乎无法掌握Java内存异常。我知道这意味着我的应用程序已经超出了虚拟机的预算,但经过Google多次搜索之后,我似乎仍然无法理解这一概念。恐怕我的应用使用了太多内存,因为每个屏幕有六个按钮选择器,每个选择器有两个位图,根据属性选项卡,每个选择器大约20 kb。在我根深蒂固的G2x上,我将虚拟机预算设置为12MB,重新启动手机并运行我的应用程序,无任何问题。我在每个onDestroy()上解绑定drawables,暗示GC也在这里运行。在模拟器中使用该应用程序一段时间后,我在DDMS屏幕上单击“原因GC”,结果为 ID = 1,堆大小6.133 MB,分配2.895MB,免费3.238 MB,%使用47.20,#对象52,623。Android了解堆大小

这是我不明白发生了什么,我的模拟器设置为24MB的虚拟机。那个号码在哪里?我遇到的实际问题是如果我将模拟器设置为16MB的虚拟机,那么我的应用程序会在第二个活动出现内存不足异常时崩溃。为什么它不会在VM设置为12 MB的情况下在我的手机上崩溃,或者在带有12 MB VM的旧HTC Magic手机上崩溃?你们也认为我的应用占用了太多的内存吗?我不知道这些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会推动它,在内存分配方面是否有这样的限制?再次感谢亚历克斯洛克伍德您的回应我将阅读并重新阅读它,直到这个东西对我有意义

回答

50

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

这就是说,你在DDMS屏幕上看不到“24 MB”的原因是因为堆没有增长到最大尺寸。这使Android可以充分利用手持设备上已有的少量内存。至于为什么你的应用程序在模拟器而不是你的手机上崩溃,那看起来很奇怪(你确定这些数字是正确的吗?)。但是,您应该记住,内存是动态管理的,并且总内存利用率是根据许多外部因素(执行垃圾收集的速度/频率等)确定的。

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

+3

+1,非常丰富的答案! –

+0

非常感谢!我从中学到了很多东西。我在问题的底部添加了一些信息 –

+0

重新解析评论“20 kb,每个听起来都很好” - AFAIK,*文件大小*无关紧要(对于压缩更好的图像,这将更小),它关乎*尺寸*是什么;在内存中,对于全彩色,每像素需要4B,对吧?因此,根据当前设备的实际所需大小加载它时调整图像的大小非常重要[android docs - 将缩小的版本加载到内存中]。 – ToolmakerSteve

16

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

在我的情况下,我在少量时间内创建并倾倒了几张图片。

很多时候我得到了OutOfMemoryError

看来Android的速度不够快,无法为我的应用增加堆空间。

我相信我通过在Manifest文件中使用largeHeap设置解决了这个问题。 随着该设置的开启,Android每次增长堆时都会留下更多可用内存,从而最大限度地降低了达到当前限制的可能性。

我不使用24mb的限制,但这个largeHeap conf是相当方便的。

你只需要设置largeHeap="true"对你AndroidManifest.xml

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

应用程序标记尽管如此,确保与图片的时候,像@Alex洛克伍德劝你小心。

+16

尽可能小心使用largeHeap =“true”。因为使用这可能会不利地影响您的应用程序性能。因为您告诉系统增加最大堆限制。发生这种情况时,垃圾收集需要更多时间。如果您检查日志,您可以看到GC暂停时间会更长。理想情况下,它应该在2-5ms之间。在这种情况下,甚至可以高达30-45ms。所以,不要因为你的内存不足而将大堆属性设置为true。将它用作最后一步。否则,它将成为性能打击。 –

+0

确实。 在我的情况下,我正在动态处理大量高质量图像,并且在使用largeHeap选项之前,我已经实现了以下代码: - 加载图像采样 - 将图像调整为完美匹配 - 清除内存中未使用的图像 - 在“磁盘”中缓存图像 我仍然遇到只有使用largeHeap选项解决的问题。 不过,只有在尝试其他所有内容后才能使用它,例如@SanalVarghese建议。 – tbraun

+0

只是增加了这一点,我一直有大量的问题与我的应用程序的内存它的大小大大增加。在这一点上它可能不会有效地集合在一起,但它本应该能够不能达到最大限度。但它永远不会释放许多资源。 由于某些原因,它在应用largeHeap选项后很好。谢谢一堆。 –