OutOfMemoryError, although vm has enough free memory

I get this weird OutOfMemoryError that raises, although dalvikvm reports enough heap space. Magazines:

12-09 14:16:05.527: D/dalvikvm(10040): GC_FOR_ALLOC freed 551K, 21% free 38000K/47687K, paused 173ms, total 173ms 12-09 14:16:05.527: I/dalvikvm-heap(10040): Grow heap (frag case) to 38.369MB for 858416-byte allocation 12-09 14:16:05.699: D/dalvikvm(10040): GC_FOR_ALLOC freed 6K, 21% free 38832K/48583K, paused 169ms, total 169ms 12-09 14:16:05.894: D/dalvikvm(10040): GC_FOR_ALLOC freed 103K, 20% free 38929K/48583K, paused 169ms, total 169ms 12-09 14:16:05.894: I/dalvikvm-heap(10040): Forcing collection of SoftReferences for 858416-byte allocation 12-09 14:16:06.074: D/dalvikvm(10040): GC_BEFORE_OOM freed 6K, 20% free 38922K/48583K, paused 182ms, total 182ms 12-09 14:16:06.074: E/dalvikvm-heap(10040): Out of memory on a 858416-byte allocation. 12-09 14:16:06.074: I/dalvikvm(10040): "AsyncTask #2" prio=5 tid=17 RUNNABLE 12-09 14:16:06.074: I/dalvikvm(10040): | group="main" sCount=0 dsCount=0 obj=0x42013580 self=0x5f2a48d8 12-09 14:16:06.074: I/dalvikvm(10040): | sysTid=10101 nice=10 sched=0/0 cgrp=apps/bg_non_interactive handle=1591062136 12-09 14:16:06.074: I/dalvikvm(10040): | schedstat=( 7305663992 4216491759 5326 ) utm=697 stm=32 core=1 12-09 14:16:06.074: I/dalvikvm(10040): at android.graphics.BitmapFactory.nativeDecodeStream(Native Method) 12-09 14:16:06.074: I/dalvikvm(10040): at android.graphics.BitmapFactory.decodeStream(BitmapFactory.java:619) 12-09 14:16:06.074: I/dalvikvm(10040): at android.graphics.BitmapFactory.decodeStream(BitmapFactory.java:691) 

As you can see right before exiting the memory, dalvikvm reports free memory 10 MB after gc. The distribution is intended for a bitmap of 800 bits. I doubt that there is a race condition between gc and raster decoding, because the reported dalvik free memory did not fall below 8 MB free memory in all log statements in the last 20-30 seconds before the failure.

The problem occurs on the Samsung Galaxy Tab 2 10.1 running Android 4.1.2. I use a modified version of the ImageFetcher classes from the Google I / O application (2012), so I already do things like inJustDecodeBounds when loading images to optimize the sampleSize parameter.

According to the documentation in Raster Memory Management, Android allocates Bitmap pixel data in the dalvik heap (since Android 3.0), so why does decoding a bitmap cause outofmemory with 10 MB of free memory ?

Has anyone seen this before, or may have an idea of ​​what is happening?

EDIT: Upon request, here is the code to download the image from the Google I / O 2012 application. In my application I just call

 mImageFetcher.loadImage(myUrl, myImageView); 

EDIT2: Relevant image decoding methods extracted from the link above to show that I am already using sample size optimization:

 public static Bitmap decodeSampledBitmapFromDescriptor( FileDescriptor fileDescriptor, int reqWidth, int reqHeight) { // First decode with inJustDecodeBounds=true to check dimensions final BitmapFactory.Options options = new BitmapFactory.Options(); options.inJustDecodeBounds = true; BitmapFactory.decodeFileDescriptor(fileDescriptor, null, options); // Calculate inSampleSize options.inSampleSize = calculateInSampleSize(options, reqWidth, reqHeight); // Decode bitmap with inSampleSize set options.inJustDecodeBounds = false; return BitmapFactory .decodeFileDescriptor(fileDescriptor, null, 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) { // Calculate ratios of height and width to requested height and // width final int heightRatio = Math.round((float) height / (float) reqHeight); final int widthRatio = Math.round((float) width / (float) reqWidth); // Choose the smallest ratio as inSampleSize value, this will // guarantee // a final image with both dimensions larger than or equal to the // requested height and width. inSampleSize = heightRatio < widthRatio ? heightRatio : widthRatio; // This offers some additional logic in case the image has a strange // aspect ratio. For example, a panorama may have a much larger // width than height. In these cases the total pixels might still // end up being too large to fit comfortably in memory, so we should // be more aggressive with sample down the image (=larger // inSampleSize). final float totalPixels = width * height; // Anything more than 2x the requested pixels we'll sample down // further. final float totalReqPixelsCap = reqWidth * reqHeight * 2; while (totalPixels / (inSampleSize * inSampleSize) > totalReqPixelsCap) { inSampleSize++; } } return inSampleSize; } 
+7
android android memory
source share
7 answers

It seems that ICS and later versions of Android do not allow your virtual machine to go to the total heap size. I saw the same thing in my application,

You can add

 android:largeHeap="true" 

into your <application>, which gives your application a much larger bunch. Not nice, but it works ...

+3
source share

About OutOfMemory (OOM) when distributing ~ 850,000 bytes, when you have 10 MB for free, which is certainly due to memory fragmentation, there is no guarantee that the heap has one continuous piece of memory larger than 850,000 bytes, and that is why you get OOM.

It seems strange that you are still getting the error, it looks like you have already done some optimizations, are you really freeing up all the memory that you are holding? I mean, you have 38 MB of heap used, what is in this memory?

Have you tried looking for image loading libraries like picasso ?

Where could there be such things as: Picasso.with(context).load(" http://i.imgur.com/DvpvklR.png ").fit().into(imageView);

(This loads and caches the image and fits and draws into the imageView, neatly!)

Update

  • You should analyze your hprof file (it should be available in the sdcard root directory since you got OOM) in MAT to find out if you have unnecessary links
  • Release these links (null them so the GC collects them)
  • Use inBitmap to reuse memory (made more powerful in KitKat, images do not have to be the same size as the previous image, just hold so much or less memory than before)
  • If you often request the same image, consider caching, for example. Lrucache
  • If they are really large bitmaps, try drawing an image (load the small square bitmaps into a large image): TileImageView (this uses GlView to draw, though ..)
+3
source share

try with this

 public static Bitmap decodeFile(File f,int WIDTH,int HIGHT){ try { //Decode image size BitmapFactory.Options o = new BitmapFactory.Options(); o.inJustDecodeBounds = true; BitmapFactory.decodeStream(new FileInputStream(f),null,o); //The new size we want to scale to final int REQUIRED_WIDTH=WIDTH; final int REQUIRED_HIGHT=HIGHT; //Find the correct scale value. It should be the power of 2. int scale=1; while(o.outWidth/scale/2>=REQUIRED_WIDTH && o.outHeight/scale/2>=REQUIRED_HIGHT) scale*=2; //Decode with inSampleSize BitmapFactory.Options o2 = new BitmapFactory.Options(); o2.inSampleSize=scale; return BitmapFactory.decodeStream(new FileInputStream(f), null, o2); } catch (FileNotFoundException e) {} return null; } 

this scales the bitmap in width and height. this function finds the correct scale according to the image resolution.

+1
source share

This code will help you, try this

 public Bitmap decodeSampledBitmapFromResource(String path, int reqWidth, int reqHeight) { // First decode with inJustDecodeBounds=true to check dimensions final BitmapFactory.Options options = new BitmapFactory.Options(); options.inJustDecodeBounds = true; BitmapFactory.decodeFile(path, options); // BitmapFactory.decodeResource(getResources(), id) // Calculate inSampleSize options.inSampleSize = calculateInSampleSize(options, reqWidth, reqHeight); // Decode bitmap with inSampleSize set options.inJustDecodeBounds = false; return BitmapFactory.decodeFile(path, options); } public 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) { // Calculate ratios of height and width to requested height and // width final int heightRatio = Math.round((float) height / (float) reqHeight); final int widthRatio = Math.round((float) width / (float) reqWidth); // Choose the smallest ratio as inSampleSize value, this will // guarantee // a final image with both dimensions larger than or equal to the // requested height and width. inSampleSize = heightRatio < widthRatio ? heightRatio : widthRatio; } return inSampleSize; } 
0
source share

I'm new, I'm not sure, but I will try to try an image like this

  final BitmapFactory.Options options = new BitmapFactory.Options(); options.inSampleSize = 8; Bitmap bm=BitmapFactory.decodeFile(strPath,options); 

Use inSampleSize to load bitmaps into memory. Using degrees 2 for inSampleSize values ​​is faster and more efficient for a decoder. However, if you plan to cache modified versions in memory or on disk, it is usually worth decrypting it to the most suitable image size anyway to save space.

0
source share

create class Imageloader

 package com.example.model; import java.io.File; import java.io.FileInputStream; import java.io.FileNotFoundException; import java.io.FileOutputStream; import java.io.InputStream; import java.io.OutputStream; import java.net.HttpURLConnection; import java.net.URL; import java.util.Collections; import java.util.Map; import java.util.WeakHashMap; import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; import android.app.Activity; import android.content.Context; import android.graphics.Bitmap; import android.graphics.BitmapFactory; import android.graphics.Matrix; import android.widget.ImageView; public class ImageLoader { MemoryCache memoryCache=new MemoryCache(); FileCache fileCache; private Map<ImageView, String> imageViews=Collections.synchronizedMap(new WeakHashMap<ImageView, String>()); ExecutorService executorService; public ImageLoader(Context context){ fileCache=new FileCache(context); executorService=Executors.newFixedThreadPool(5); } final int stub_id=R.drawable.load; public void DisplayImage(String url, ImageView imageView) { imageViews.put(imageView, url); Bitmap bitmap=memoryCache.get(url); if(bitmap!=null) imageView.setImageBitmap(bitmap); else { queuePhoto(url, imageView); imageView.setImageResource(stub_id); } } private void queuePhoto(String url, ImageView imageView) { PhotoToLoad p=new PhotoToLoad(url, imageView); executorService.submit(new PhotosLoader(p)); } private Bitmap getBitmap(String url) { File f=fileCache.getFile(url); //from SD cache Bitmap b = decodeFile(f); if(b!=null) return b; //from web try { Bitmap bitmap=null; URL imageUrl = new URL(url); // System.out.println("url :"+imageUrl); HttpURLConnection conn = (HttpURLConnection)imageUrl.openConnection(); conn.setConnectTimeout(30000); conn.setReadTimeout(30000); conn.setInstanceFollowRedirects(true); InputStream is=conn.getInputStream(); OutputStream os = new FileOutputStream(f); Utils.CopyStream(is, os); os.close(); bitmap = decodeFile(f); return bitmap; } catch (Throwable ex){ ex.printStackTrace(); if(ex instanceof OutOfMemoryError) memoryCache.clear(); return null; } } //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; o.inPurgeable = true; // Tell to garbage collector that whether it needs free memory, the Bitmap can be cleared o.inTempStorage = new byte[32 * 1024]; BitmapFactory.decodeStream(new FileInputStream(f),null,o); //Find the correct scale value. It should be the power of 2. final int REQUIRED_SIZE=70; int width_tmp=o.outWidth, height_tmp=o.outHeight; int scale=1; while(true){ if(width_tmp/2<REQUIRED_SIZE || height_tmp/2<REQUIRED_SIZE) break; width_tmp/=2; height_tmp/=2; long heapSize = Runtime.getRuntime().maxMemory(); long heapsize1=(heapSize/(1024*1024)); if(heapsize1>95) { scale*=1; // System.out.println("scale1 :"); }else if(heapsize1>63 && heapsize1<=95){ scale*=2; // System.out.println("scale2 :"); }else if(heapsize1>31 && heapsize1<=63){ scale*=2; // System.out.println("scale22 :"); }else if(heapsize1>0 && heapsize1<=31){ scale*=2; // System.out.println("scale23 :"); } /*else if(heapsize1>31 && heapsize1<=63){ scale*=2; // System.out.println("scale2 :"); }else if(heapsize1>0 && heapsize1<=31){ scale*=2; // System.out.println("scale2 :"); }*/ } //decode with inSampleSize BitmapFactory.Options o2 = new BitmapFactory.Options(); o2.inPurgeable = true; // Tell to garbage collector that whether it needs free memory, the Bitmap can be cleared o2.inTempStorage = new byte[32 * 1024]; o2.inSampleSize=scale; Bitmap bitmap1=BitmapFactory.decodeStream(new FileInputStream(f), null, o2); // System.out.println("width : "+bitmap1.getWidth()+ " height : "+bitmap1.getHeight()); /* if(bitmap1.getHeight()>=bitmap1.getWidth()) { bitmap1 = Bitmap.createScaledBitmap(bitmap1, bitmap1.getHeight(),bitmap1.getWidth(), true); }else{ //bmp = Bitmap.createScaledBitmap(bmp, (int) height2,width, true); Matrix matrix = new Matrix(); matrix.postRotate(270); bitmap1 = Bitmap.createBitmap(bitmap1 , 0, 0, bitmap1 .getWidth(), bitmap1 .getHeight(), matrix, true); }*/ return bitmap1; } catch (FileNotFoundException e) {} return null; } //Task for the queue private class PhotoToLoad { public String url; public ImageView imageView; public PhotoToLoad(String u, ImageView i){ url=u; imageView=i; } } class PhotosLoader implements Runnable { PhotoToLoad photoToLoad; PhotosLoader(PhotoToLoad photoToLoad){ this.photoToLoad=photoToLoad; } @Override public void run() { if(imageViewReused(photoToLoad)) return; Bitmap bmp=getBitmap(photoToLoad.url); memoryCache.put(photoToLoad.url, bmp); if(imageViewReused(photoToLoad)) return; BitmapDisplayer bd=new BitmapDisplayer(bmp, photoToLoad); Activity a=(Activity)photoToLoad.imageView.getContext(); a.runOnUiThread(bd); } } boolean imageViewReused(PhotoToLoad photoToLoad){ String tag=imageViews.get(photoToLoad.imageView); if(tag==null || !tag.equals(photoToLoad.url)) return true; return false; } //Used to display bitmap in the UI thread class BitmapDisplayer implements Runnable { Bitmap bitmap; PhotoToLoad photoToLoad; public BitmapDisplayer(Bitmap b, PhotoToLoad p){ bitmap=b;photoToLoad=p; } public void run() { if(imageViewReused(photoToLoad)) return; if(bitmap!=null) photoToLoad.imageView.setImageBitmap(bitmap); else photoToLoad.imageView.setImageResource(stub_id); } } public void clearCache() { memoryCache.clear(); fileCache.clear(); } } 

use

or use this link example lazy bootloader

0
source share

First of all, all the answers will help you reduce memory consumption, but none of us can really help with your application, because we do not know all the code.

I will tell you the experience that I had with our application. We tested too much OOM, and we tried the example of Picasso, BitmapFun from Android, and finally I decided to profile my application.

Android Studio provides you with a tool called “Monitor”, in which you can track how much memory your application occupies in RAM, in every action that you call, every rotation, etc.

You can export the profiling you just did using the HEAP Dump, and then import it into the Eclipse Memory Analyzer Tool. There you can run an object leak detector (or something like that. This will help you show which objects have leaked to RAM.

those. We used the old method (OnRetainCustomConfig ...), where you can just save the link to your objects. The problem was that the new action received references to the old Activity object, and because of this, the GC did not successfully delete the old actions, because it suggested that they were still in use.

I hope my comment helps you

0
source share

All Articles