【问题标题】:Android: Out of memory errorAndroid:内存不足错误
【发布时间】:2013-01-17 03:37:51
【问题描述】:

当我最小化我的 Android 应用程序大约 4 或 5 次时,我总是收到以下错误:

02-01 19:24:11.980: E/dalvikvm-heap(22362): Out of memory on a 3686416-byte allocation.
02-01 19:24:12.000: E/dalvikvm(22362): Out of memory: Heap Size=62755KB, Allocated=55237KB, Limit=65536KB
02-01 19:24:12.000: E/dalvikvm(22362): Extra info: Footprint=62435KB, Allowed Footprint=62755KB, Trimmed=2144KB
02-01 19:24:12.000: E/Bitmap_JNI(22362): Create Bitmap Failed.    
02-01 19:24:12.000: E/Bitmap_JNI(22362): Failed to create SkBitmap!
02-01 19:24:12.000: E/AndroidRuntime(22362): FATAL EXCEPTION: main
02-01 19:24:12.000: E/AndroidRuntime(22362): java.lang.OutOfMemoryError: (Heap Size=62755KB, Allocated=55237KB)
02-01 19:24:12.000: E/AndroidRuntime(22362):    at android.graphics.Bitmap.nativeCreateScaledBitmap(Native Method)
02-01 19:24:12.000: E/AndroidRuntime(22362):    at android.graphics.Bitmap.createScaledBitmap(Bitmap.java:744)
02-01 19:24:12.000: E/AndroidRuntime(22362):    at de.vauge.mb.Utils.getResizedBitmap(Utils.java:56)
02-01 19:24:12.000: E/AndroidRuntime(22362):    at de.vauge.mb.MenuView.initialize(MenuView.java:74)
02-01 19:24:12.000: E/AndroidRuntime(22362):    at de.vauge.mb.MenuView$1.handleMessage(MenuView.java:137)
02-01 19:24:12.000: E/AndroidRuntime(22362):    at android.os.Handler.dispatchMessage(Handler.java:99)
02-01 19:24:12.000: E/AndroidRuntime(22362):    at android.os.Looper.loop(Looper.java:156)
02-01 19:24:12.000: E/AndroidRuntime(22362):    at android.app.ActivityThread.main(ActivityThread.java:5045)
02-01 19:24:12.000: E/AndroidRuntime(22362):    at java.lang.reflect.Method.invokeNative(Native Method)
02-01 19:24:12.000: E/AndroidRuntime(22362):    at java.lang.reflect.Method.invoke(Method.java:511)
02-01 19:24:12.000: E/AndroidRuntime(22362):    at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:784)
02-01 19:24:12.000: E/AndroidRuntime(22362):    at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:551)
02-01 19:24:12.000: E/AndroidRuntime(22362):    at dalvik.system.NativeStart.main(Native Method)

我的应用程序仅包含一个 Activity,其中包含 7 个不同的自行编写的视图(所有视图都包含一些位图),并且在不需要它们时它们被切换为不可见(可能不是很好的样式,但它适用于我直到现在......)。这些视图中的每一个都有一个destroy() 函数,用于回收其中使用的所有位图,并且MainActivity 的onDestroy() 调用所有这些destroy() 函数。此外,我没有使用任何静态位图。

那么,除了回收所有位图而不使用静态位图之外,我还有什么可以尝试的吗?

【问题讨论】:

    标签: android memory bitmap heap-memory


    【解决方案1】:

    嗯。 Android 上的位图可能有点棘手。您能否提供有关位图来源及其大小的更好信息?

    否则,我建议您查看以下内容:

    1. 如果您正在加载远程图像,请查看fresco。您也可以查看Picasso。我个人曾经很喜欢ImageLoader,但是现在已经不再维护了。

    2. 如果您使用的 inPurgable 标志曾经是推荐选项,请尝试找到解决方法,因为它实际上会导致为每个图像分配更多内存。

    3. 如果您经常解码小型本地资产,请考虑将您的可绘制对象保存在哈希图中,并在需要时重新使用它们。更少的GC。

    4. 如果您想对您的应用程序进行子类化,您可以使用OnLowMemory 调用来了解您何时可能真正需要清理(主要用于调试,而不是现实生活中的情况) ...如果还不算太晚... :)

    5. 查看 Chris Banes 的博客。 This 是一个非常有趣的内存缓存解决方案

    6. 在需要和可能的情况下实现您调用的内存修剪器。

    7. 另一个不足为奇的优化是尽可能使用更小的对象...想想您的最小数据模型和图像大小,并尝试为这些提供符合要求的 API。

    【讨论】:

      【解决方案2】:

      对于 Ben Max 评论中的 #3,我制作了两个有用的类:

      public abstract class SoftReferenceStorage<K, V>{
      private static HashMap<Object, SoftReference<Object>> objectsHash = new HashMap<Object, SoftReference<Object>>();
      
      @SuppressWarnings("unchecked")
      public V get(K key) {
          if (objectsHash.containsKey(key)) {
              SoftReference<Object> ref = objectsHash.get(key);
              if (ref.get() == null) {
                  objectsHash.put(key, new SoftReference<Object>(createValueForKey(key)));
                  return (V)objectsHash.get(key).get();
              } else {
                  return (V)ref.get();
              }
          } else {
              objectsHash.put(key, new SoftReference<Object>(createValueForKey(key)));
              return (V)objectsHash.get(key).get();
          }
      }
      
      protected abstract V createValueForKey(K key);
      }
      

      public class FrequentlyUsedBitmapResources extends SoftReferenceStorage<Integer, Bitmap>{
      private static FrequentlyUsedBitmapResources instance = null;
      
      private Resources resources;
      
      public FrequentlyUsedBitmapResources(Resources resources) {
          super();
          this.resources = resources;
      }
      
      public static FrequentlyUsedBitmapResources getInstance() {
          if (instance == null) {
              instance = new FrequentlyUsedBitmapResources(HiDriveApp.getContext().getResources());
          }
          return instance;
      }
      
      @Override
      protected Bitmap createValueForKey(Integer resId) {
          return BitmapFactory.decodeResource(resources, resId);
      }
      }
      

      可以这样使用:

      Bitmap b = FrequentlyUsedBitmapResources.getInstance().get(R.drawable.overview_photo_placeholder);
      

      【讨论】:

        【解决方案3】:

        确保将它们加载到 onCreate() 而不是 onStart()onResume()。听起来每次您恢复时它们都会重新加载,但它们并没有被销毁,因为当您最小化应用程序时不会调用 onDestroy()

        【讨论】:

        • Mhh.. 它们在 onCreate() 中加载。我刚刚意识到,将它们最小化根本不是问题。只有当我锁定屏幕时,应用程序似乎被破坏或至少调用了 onDestroy(),当我多次锁定和解锁手机时,错误发生......有什么想法吗?
        • 您确定在锁定手机时正在呼叫onDestroy() 吗?无论哪种方式,如果您只在onCreate() 中制作它们都没有关系。我能想到的唯一另一件事是在每个位图上制作一个调用isRecycled() 的日志打印,以确保它们实际上被回收。
        【解决方案4】:

        如果图像在本地设备上(即与您的代码合并或来自用户图像库),那么我可能会选择不让它们简单地隐藏或不隐藏,而是直接将它们带入从磁盘。事实证明,所有这些设备本质上都是基于闪存的,与主轴磁盘相比,速度非常快。最有可能的是,用户无法感受到磁盘 IO 对图像的性能影响。

        同样,您可以限制您在任何时候在内存中保存的图像数量。

        我同意你也应该看看 Tim 的评估。

        【讨论】:

          猜你喜欢
          • 2012-03-07
          • 2013-11-01
          • 1970-01-01
          • 2014-12-30
          • 2013-01-17
          • 1970-01-01
          • 2013-05-13
          • 2011-12-08
          相关资源
          最近更新 更多