31

我偶尔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();
    }
}
4

8 回答 8

44

也许这对你有帮助?

添加清单

安卓 > v3

<application
    ....
       android:largeHeap="true"> 
于 2014-06-03T06:29:12.053 回答
37

常见修复:

1.修复你的上下文:

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

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

使用此表作为适合上下文的快速指南: 在此处输入图像描述

关于上下文的原始文章在这里

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

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

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

3.检查图像和位图的使用:

如果您使用的是 Square 的Picasso 库,我发现我没有使用.fit().

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);

于 2016-02-10T23:13:35.767 回答
9

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

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;
}
于 2014-01-09T05:17:05.360 回答
1

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

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());
于 2014-01-09T05:15:17.333 回答
1

最终位图微笑 = 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 和活动中传递参数

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

于 2014-01-09T06:01:40.593 回答
1

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

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

分析后,您可以阅读如何从有效地显示位图中有效地加载图像

还有几个流行的图像加载库,例如通用图像加载器毕加索,可以轻松完成您需要的操作。

于 2014-01-09T08:22:38.540 回答
0

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

于 2015-07-19T19:03:52.467 回答
0

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

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

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

于 2020-08-04T15:36:42.797 回答