【问题标题】:Android OutOfMemoryError:?Android OutOfMemoryError:?
【发布时间】:2014-01-09 05:08:08
【问题描述】:

我偶尔会在我的一个应用程序中收到OutOfMemoryError: (Heap Size=49187KB, Allocated=41957KB)。我该怎么做才能诊断?

  01-09 10:32:02.079: E/dalvikvm(8077): Out of memory: Heap Size=49187KB, Allocated=41957KB, Limit=49152KB
  01-09 10:32:02.079: E/dalvikvm(8077): Extra info: Footprint=48611KB, Allowed Footprint=49187KB, Trimmed=7852KB
  01-09 10:32:02.079: D/skia(8077): --- decoder->decode returned false
  01-09 10:32:02.079: D/AndroidRuntime(8077): Shutting down VM
  01-09 10:32:02.079: W/dalvikvm(8077): threadid=1: thread exiting with uncaught exception (group=0x40a97228)
  01-09 10:32:02.079: E/AndroidRuntime(8077): FATAL EXCEPTION: main
  01-09 10:32:02.079: E/AndroidRuntime(8077): java.lang.OutOfMemoryError: (Heap Size=49187KB, Allocated=41957KB)
  01-09 10:32:02.079: E/AndroidRuntime(8077):   at android.graphics.BitmapFactory.nativeDecodeAsset(Native Method)
  01-09 10:32:02.079: E/AndroidRuntime(8077):   at android.graphics.BitmapFactory.decodeResourceStream(BitmapFactory.java:486)
  01-09 10:32:02.079: E/AndroidRuntime(8077):   at android.graphics.drawable.Drawable.createFromResourceStream(Drawable.java:773)
  01-09 10:32:02.079: E/AndroidRuntime(8077):   at android.content.res.Resources.loadDrawable(Resources.java:2044)
  01-09 10:32:02.079: E/AndroidRuntime(8077):   at android.content.res.Resources.getDrawable(Resources.java:675)
  01-09 10:32:02.079: E/AndroidRuntime(8077):   at android.view.View.setBackgroundResource(View.java:11776)
  01-09 10:32:02.079: E/AndroidRuntime(8077):   at com.blsk.bigtoss.ImageLoader.DisplayImage(ImageLoader.java:81)
  01-09 10:32:02.079: E/AndroidRuntime(8077):   at com.blsk.bigtoss.MatchActivity$MatchAsyncTask.onPostExecute(MatchActivity.java:1768)
  01-09 10:32:02.079: E/AndroidRuntime(8077):   at android.os.AsyncTask.finish(AsyncTask.java:602)
  01-09 10:32:02.079: E/AndroidRuntime(8077):   at android.os.AsyncTask.access$600(AsyncTask.java:156)
  01-09 10:32:02.079: E/AndroidRuntime(8077):   at android.os.AsyncTask$InternalHandler.handleMessage(AsyncTask.java:615)
  01-09 10:32:02.079: E/AndroidRuntime(8077):   at android.os.Handler.dispatchMessage(Handler.java:99)
  01-09 10:32:02.079: E/AndroidRuntime(8077):   at android.os.Looper.loop(Looper.java:156)
  01-09 10:32:02.079: E/AndroidRuntime(8077):   at android.app.ActivityThread.main(ActivityThread.java:4987)
  01-09 10:32:02.079: E/AndroidRuntime(8077):   at java.lang.reflect.Method.invokeNative(Native Method)
  01-09 10:32:02.079: E/AndroidRuntime(8077):   at java.lang.reflect.Method.invoke(Method.java:511)
  01-09 10:32:02.079: E/AndroidRuntime(8077):   at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:784)
  01-09 10:32:02.079: E/AndroidRuntime(8077):   at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:551)
  01-09 10:32:02.079: E/AndroidRuntime(8077):   at dalvik.system.NativeStart.main(Native Method)
  01-09 10:32:02.099: E/EmbeddedLogger(1612): App crashed! Process: com.blsk.bigtoss
  01-09 10:32:02.099: E/EmbeddedLogger(1612): App crashed! Package: com.blsk.bigtoss v6 (1.2)
  01-09 10:32:02.129: E/EmbeddedLogger(1612): Application Label: Cricket

这是发生的地方:

LinearLayout resultMatchHeaderContainer = new LinearLayout(activity); 

if (!resultImagePath.equals("")) {   
    imageLoader.DisplayImage(resultImagePath,resultMatchHeaderContainer, -1,modifiedHeight, R.drawable.matches_placeholder_result2x);
} else {
    try {
        resultMatchHeaderContainer.setBackgroundResource(R.drawable.matches_placeholder_result2x); 
    } catch (OutOfMemoryError e) {         
        e.printStackTrace();
    }
}

【问题讨论】:

  • 您正在尝试将位图加载到 ImageView 中。显然,您有时加载的位图太大而无法放入内存。您需要在阅读时缩小图像。看看 inSampleSize
  • @NagarjunaReddy 你的问题解决了吗?
  • 还没有尝试...@dipali
  • 什么?你的prblm没有解决吗?
  • 该错误只发生在某个时间而不是所有时间@dipali

标签: android out-of-memory image-load


【解决方案1】:

也许这对你有帮助?

添加清单

安卓 > v3

<application
    ....
       android:largeHeap="true"> 

【讨论】:

  • 并非在所有情况下都建议使用 largeHeap,请谨慎使用,它可能会减慢其他正在运行的应用程序,并且还会影响您的应用程序的响应性,因为垃圾收集器会更频繁地被请求。有关更多信息,请查看来自 google i/o youtube.com/watch?v=_CruQY55HOk 的演讲
  • 永远不要仅仅因为内存用完而需要快速修复就请求大堆——只有在确切知道所有内存被分配到哪里以及为什么必须分配时才应该使用它保留。然而,即使您确信您的应用程序可以证明大堆是合理的,您也应该尽可能避免请求它。见developer.android.com/training/articles/memory.html
【解决方案2】:

常见修复:

1。修正你的上下文:

尝试使用适当的上下文:例如,由于 Toast 可以在许多活动中看到,而不是只在一个活动中看到,所以使用 getApplicationContext() 表示 toast,并且即使活动结束,服务也可以继续运行,启动服务:

Intent myService = new Intent(getApplicationContext(), MyService.class)

使用此表作为适合上下文的快速指南:

article on context here

2。检查您是否真的完成了服务。

例如,我有一个使用谷歌位置服务 API 的 intentService。我忘了打googleApiClient.disconnect();

//Disconnect from API onDestroy()
    if (googleApiClient.isConnected()) {
        LocationServices.FusedLocationApi.removeLocationUpdates(googleApiClient, GoogleLocationService.this);
        googleApiClient.disconnect();
    }

3。检查图像和位图的使用情况:

如果您使用的是 square 的 library Picasso,我发现我不使用 .fit() 会导致内存泄漏,这大大减少了我的内存占用,从平均 50MB 减少到不到 19MB:

Picasso.with(ActivityExample.this)                   //Activity context
                .load(object.getImageUrl())           
                .fit()                                //This avoided the OutOfMemoryError
                .centerCrop()                         //makes image to not stretch
                .into(imageView);

4。如果您使用广播接收器,请取消注册。

5。如果你使用java.util.Observer(观察者模式):

请务必使用deleteObserver(observer);

【讨论】:

    【解决方案3】:

    您可以执行以下操作来避免这种情况。

    Drawable drawable = resultMatchHeaderContainer.getDrawable();
    
    if (drawable instanceof BitmapDrawable) {
        BitmapDrawable bitmapDrawable = (BitmapDrawable) drawable;
        if (bitmapDrawable != null) {
            Bitmap bitmap = bitmapDrawable.getBitmap();
    
            if (bitmap != null && !bitmap.isRecycled())
               bitmap.recycle();
        }
    }
    

    在 Imageview 中加载 Bitmap 总是导致内存不足的问题,这种情况很常见,因此我们必须非常小心地处理 imageview 和位图。您可以做的是在为您的图像视图设置任何背景位图时,首先获取可绘制对象并回收它,以便将其从内存中删除,然后设置新的位图。这将有助于避免任何 OOM 问题。进一步。您可以使用 BitmapFactoryOptions 来减小位图的大小。喜欢:

    // decodes image and scales it to reduce memory consumption
    private Bitmap decodeFile(File f) {
        try {
            // decode image size
            BitmapFactory.Options o = new BitmapFactory.Options();
            o.inJustDecodeBounds = true;
            FileInputStream stream1 = new FileInputStream(f);
            BitmapFactory.decodeStream(stream1, null, o);
            stream1.close();
    
            // Find the correct scale value. It should be the power of 2.
            int width_tmp = o.outWidth, height_tmp = o.outHeight;
            int scale = 1;
            while (true) {
                if (width_tmp / 2 < REQUIRED_WIDTH
                        || height_tmp / 2 < REQUIRED_HIGHT)
                    break;
                width_tmp /= 2;
                height_tmp /= 2;
                scale *= 2;
            }
    
            // decode with inSampleSize
            BitmapFactory.Options o2 = new BitmapFactory.Options();
            o2.inSampleSize = scale;
            FileInputStream stream2 = new FileInputStream(f);
            Bitmap bitmap = BitmapFactory.decodeStream(stream2, null, o2);
            stream2.close();
            return bitmap;
        } catch (FileNotFoundException e) {
        } catch (IOException e) {
            e.printStackTrace();
        }
        return null;
    }
    

    【讨论】:

    • 为什么它被否决,让我也知道原因..谁投了反对票,请说明原因..
    • 虽然这在理论上是个好主意,但在实践中却不是那么好用。对 Recycle() 的调用只是提示 GC 回收内存。它可能不会在您创建新图像时被回收,因此仍可能导致您的应用程序崩溃。更重要的是,在接下来的几个 CG 循环中,GC 会自动回收内存。
    • 真的为什么有人不赞成,他/她是真正的书呆子..大多数时候它有效..它对我有用..在我的项目中..
    • 我正要回答同样的问题所以+1
    • 谢谢。沙基布·阿亚兹。 @TheIT,您的意思是位图上的回收方法没有用,或者如果那样的话,我们应该在哪种情况下使用它?
    【解决方案4】:

    在将图像加载到内存之前使用压缩图像

    Bitmap original = BitmapFactory.decodeStream(getAssets().open("1024x768.jpg"));
    ByteArrayOutputStream out = new ByteArrayOutputStream();
    original.compress(Bitmap.CompressFormat.PNG, 100, out);
    Bitmap decoded = BitmapFactory.decodeStream(new ByteArrayInputStream(out.toByteArray()));
    
    Log.e("Original   dimensions", original.getWidth()+" "+original.getHeight());
    Log.e("Compressed dimensions", decoded.getWidth()+" "+decoded.getHeight());  
    

    如果您从资源中获取位图,在这种情况下,位图尺寸将取决于手机屏幕密度

    Bitmap bitmap=((BitmapDrawable)getResources().getDrawable(R.drawable.img_1024x768)).getBitmap();
    Log.e("Dimensions", bitmap.getWidth()+" "+bitmap.getHeight());
    

    【讨论】:

    【解决方案5】:

    这可能有多种原因,您可能将对代码其他部分的引用保留太久。您可能正在加载大型位图,同时持有许多参考会给您带来 OOM 等。

    通常当发生 OOM 时,会在 sdcard 的根目录(如果 sdcard 不存在则为内部存储)创建 hprof(堆的快照),可以通过以下工具读取strong>Eclipse MAT(如果您使用 Eclipse,则包含在 android 工具中)。第一个可能需要使用hprof-conv 工具转换 hprof。这是一个如何使用 Eclipse MAT 的教程:Investigating Your RAM Usage在 Eclipse MAT 中加载 hprof 时,首先阅读泄漏嫌疑人报告是一个很好的选择

    分析后,您可以了解如何从Displaying Bitmaps Efficiently 有效加载图像

    还有几个流行的图像加载库,例如universal image loaderpicasso,可以轻松满足您的需求。

    【讨论】:

    • @NagarjunaReddy 更新了这个答案,提供了更多你可以做什么的想法。我将从分析hprof 开始,看看您是否持有太多某种类型的参考资料,如果不是这种情况,您可以考虑使用一些图像库来缓存图像并将它们调整为您需要的视图大小轻松满足需求。
    【解决方案6】:

    最终的位图微笑 = BitmapFactory.decodeResource(getResources(), R.drawable.emo_im_happy);

    打电话

    String pathname=BitMapToString(smile);

    然后调用

    setImagesNew(linearview,pathname,activity);
    

    ...

    public String BitMapToString(Bitmap bitmap) { 
          ByteArrayOutputStream baos = new ByteArrayOutputStream();
                 bitmap.compress(Bitmap.CompressFormat.PNG, 100, baos); 
            byte[] b = baos.toByteArray();
          String temp = Base64.encodeToString(b, Base64.DEFAULT);
          return temp;
     }
    
    
     public static void setImagesNew(LinearLayout linearLayout, String pathName,
                    Activity activity) {
    
                Bitmap bmp = decodeSampledBitmapFromResource(pathName,
                        getDeviceWidth(activity), getDeviceHeight(activity));
    
    linearLayout.setBackgroundDrawable(bmp);
    
    
                bmp = null;
                System.gc();
                Runtime.getRuntime().gc();
    
            }
        public static Bitmap decodeSampledBitmapFromResource(String pathName,
                int reqWidth, int reqHeight) {
    
            // First decode with inJustDecodeBounds=true to check dimensions
            final BitmapFactory.Options options = new BitmapFactory.Options();
            options.inJustDecodeBounds = true;
            BitmapFactory.decodeFile(pathName, options);
    
            // Calculate inSampleSize
            options.inSampleSize = calculateInSampleSize(options, reqWidth,
                    reqHeight);
    
            // Decode bitmap with inSampleSize set
            options.inJustDecodeBounds = false;
            return BitmapFactory.decodeFile(pathName, options);
        }
    
        public static int calculateInSampleSize(BitmapFactory.Options options,
                int reqWidth, int reqHeight) {
            // Raw height and width of image
            final int height = options.outHeight;
            final int width = options.outWidth;
            int inSampleSize = 1;
    
            if (height > reqHeight || width > reqWidth) {
    
                final int halfHeight = height / 2;
                final int halfWidth = width / 2;
    
                // Calculate the largest inSampleSize value that is a power of 2 and
                // keeps both
                // height and width larger than the requested height and width.
                while ((halfHeight / inSampleSize) > reqHeight
                        && (halfWidth / inSampleSize) > reqWidth) {
                    inSampleSize *= 2;
                }
            }
    
            return inSampleSize;
        }
    
        @SuppressLint("NewApi")
        public static int getDeviceWidth(Activity activity) {
            int deviceWidth = 0;
    
            Point size = new Point();
            WindowManager windowManager = activity.getWindowManager();
    
            if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.HONEYCOMB) {
                windowManager.getDefaultDisplay().getSize(size);
                deviceWidth = size.x;
            } else {
                Display display = windowManager.getDefaultDisplay();
                deviceWidth = display.getWidth();
            }
            return deviceWidth;
        }
    
        @SuppressLint("NewApi")
        public static int getDeviceHeight(Activity activity) {
            int deviceHeight = 0;
    
            Point size = new Point();
            WindowManager windowManager = activity.getWindowManager();
    
            if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.HONEYCOMB) {
                windowManager.getDefaultDisplay().getSize(size);
                deviceHeight = size.y;
            } else {
                Display display = windowManager.getDefaultDisplay();
                deviceHeight = display.getHeight();
            }
            return deviceHeight;
        }
    

    请将所有函数放入您的活动中并仅调用setImageNew() 并在 imageview 、sdcardpathname 和活动中传递参数

    我希望在您实现此代码后它不会崩溃。 因为我遇到了和你一样的问题..

    【讨论】:

    • 最终位图微笑 = BitmapFactory.decodeResource(getResources(), R.drawable.emo_im_happy);
    • public String BitMapToString(Bitmap bitmap) { ByteArrayOutputStream baos = new ByteArrayOutputStream(); bitmap.compress(Bitmap.CompressFormat.PNG, 100, baos);字节[] b = baos.toByteArray();字符串 temp = Base64.encodeToString(b, Base64.DEFAULT);返回温度; }
    • 调用BitMapToString(smile);
    【解决方案7】:

    如果位图资源未正确处理,可能会发生这种情况。最好阅读尺寸以查看它是否适合内存。 http://developer.android.com/training/displaying-bitmaps/load-bitmap.html

    【讨论】:

      【解决方案8】:

      我通过以下方式解决了同样的问题:

      • 将图像尺寸从 5000x2500 减少到 320x240 - 1080x720(几个 drawables 文件夹)
      • 将 PNG 图像转换为 WEBP 格式。它的大小减少了 50 倍(从 1.2 MB 到 30 KB)。

      https://developer.android.com/studio/write/convert-webp#convert_images_to_webp

      【讨论】:

        猜你喜欢
        • 2011-05-16
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2018-02-07
        • 2011-11-09
        • 2023-03-20
        • 1970-01-01
        相关资源
        最近更新 更多