How to recycle a bitmap? - bitmap size exceeds VM budget - android

I have this function that loads a big bitmap from SD and rotates it.
Yet after the second or third rotating I get a bitmap size exceeds VM budget error.
Any ideas why? I do recycle the old bitmap, don't it?
public void next(String s, int d)
{
if ( mBitmap!=null ) { mBitmap.recycle(); }
deg = deg + d;
mBitmap = BitmapFactory.decodeFile(s);
Matrix matrix = new Matrix();
matrix.postRotate(deg);
mBitmap = Bitmap.createBitmap(mBitmap, 0, 0, mBitmap.getWidth() , mBitmap.getHeight(), matrix, true);
Thanks!

Its not uncommon for outof memory errors when you dont use bitmaps properly.
Bitmaps take up a lot of memory, especially for rich images like photographs. For example, the camera on the Galaxy Nexus takes photos up to 2592x1936 pixels (5 megapixels). If the bitmap configuration used is ARGB_8888 (the default from the Android 2.3 onward) then loading this image into memory takes about 19MB of memory (2592*1936*4 bytes), immediately exhausting the per-app limit on some devices.
There is a great android guide on
how to use Bitmaps efficiently.
Following the guide, you should be able to reduce your memory consumption dramatically with out losing any visible quality, avoiding unecessary crashes.

code seems fine.
however , this exception also depends on the size of the bitmap (resolution and bitmap format) , and other memory consuming objects.

Related

Android Resources GC Issue

I am using
Drawable drawable = res.getDrawable(id);
Bitmap bitmap = Bitmap.createBitmap(width, height, Bitmap.Config.ARGB_4444);
Canvas canvas = new Canvas(bitmap);
bitmap.eraseColor(0);
drawable.setBounds(0,0, width, height);
drawable.draw(canvas);
return load(bitmap, linear);
to load a drawable from a resource id into OpenGL with a given width, and height. (Using
android.opengl.GLUtils.texImage2D(GL_TEXTURE_2D, 0, bitmap, 0);
)
The load function does the GL-calls, and calls also bitmap.recycle().
I specify width and height myself, because Android would match the resolution to the screen size, which I don't want.
Now my problem (this part is all working fine):
if I start my app for the first time, from Android Studio, everything works; HOWEVER if I want to restart it, it crashes because of OutOfMemoryError. I am doing the exactly same calls in both cases.
I located the issue to be in the resource management of Android, as you can see in the heap analysis:
my most expensive allocations
My images are way smaller than 9 MB each in raw (512x512, RGBA, so 1 MB).
How can I prevent Android from storing these large byte arrays, which probably are meant as some kind of cache; which however doesn't run on first start after app installation?
I am testing on Android 6.0.1, API Version 23, Galaxy S5.
Implementation of texImage2D looks like this:
public static void texImage2D(int target, int level, int internalformat,
Bitmap bitmap, int border) {
if (bitmap == null) {
throw new NullPointerException("texImage2D can't be used with a null Bitmap");
}
if (bitmap.isRecycled()) {
throw new IllegalArgumentException("bitmap is recycled");
}
if (native_texImage2D(target, level, internalformat, bitmap, -1, border)!=0) {
throw new IllegalArgumentException("invalid Bitmap format");
}
}
It doesn't look like it's recycling anything. Are you sure you are not loading a huge bitmap into memory? Two calls of those are more than enough to guarantee a huge explosion in your app, if not just one (I've seen it happen many times in my app). Remember, restarting your activity does not mean restarting your proccess.
Run the Android Profiler before the first load and check how much memory it takes.
Also, you can cache and reuse bitmaps yourself.
I solved it (myself) by putting the files into the raw folder of the resource directory, and loading them using
fun loadBitmap(res: Resources, rawId: Int): Bitmap {
val inputStream = BufferedInputStream(res.openRawResource(rawId))
return BitmapFactory.decodeStream(inputStream)
}
and then calling
load(bitmap, linear);
and
bitmap.recycle()
like before.
Luckily those all were png/jpeg files, so I didn't need the additional features of the drawables folder. Using this, they'll automatically use their right resolution.
My Java RAM allocation is now back on 25 MB to 35 MB instead of the 110 MB when using the old way :).

Android out of memory error while rotate a bitmap

I am currently working on a project in which one I would like to rotate a bitmap.
The first time, I create my bitmap with the following code :
myBitmap = BitmapFactory.decodeResource(getResources(), drawableResource);
Then I rotate the bitmap using the following code :
final Matrix matrix = new Matrix();
matrix.postRotate(currentRotate);
myBitmap = Bitmap.createBitmap(myBitmap, 0, 0, directionBitmap.getWidth(), directionBitmap.getHeight(), matrix, true);
I works, but after several times, the memory increases and I have the following exception :
java.lang.OutOfMemoryError: Failed to allocate a 119071756 byte
allocation with 16775968 free bytes and 96MB until OOM
It seems that the old bitmaps are still in memory. How to delete/recycle them in order to save the memory ?
Thank your for your help.
I can suggest you to use Glide library from Github.
This library works in background threads. Anyway you can perform your rotation on Background like this:
runOnUiThread(new Runnable(final Matrix matrix = new Matrix();
matrix.postRotate(currentRotate);
myBitmap = Bitmap.createBitmap(myBitmap, 0, 0, directionBitmap.getWidth(), directionBitmap.getHeight(), matrix, true);
)
In my case, the problem was with the size of the bitmaps I was using. The bitmaps I was using were of very high pixels for the given device. In such case, Android system has to descale them to lower pixel density i.e. the one that will suit your device. When you rotate the bitmap, Android system takes up a lot of memory to descale it to lower pixels. Also, Android gets busy and in some cases results in UI thread blocking.
Also, increase the heap size in your manifest file.

Android Bitmap.createScaledBitmap throws java.lang.OutOfMemoryError mostly on Jelly Bean 4.1

My app main purpose is to display images in following fashion as shown in image
private void setSelectedImage(int selectedImagePosition)
{
BitmapDrawable bd = (BitmapDrawable) drawables.get(selectedImagePosition);
Bitmap b = Bitmap.createScaledBitmap(bd.getBitmap(), (int) (bd.getIntrinsicHeight() * 0.9), (int) (bd.getIntrinsicWidth() * 0.7), false);
selectedImageView.setImageBitmap(b);
selectedImageView.setScaleType(ScaleType.FIT_XY);
}
Detailed code can be find here
exception is thrown at following line
Bitmap b = Bitmap.createScaledBitmap(bd.getBitmap(), (int) (bd.getIntrinsicHeight() * 0.9), (int) (bd.getIntrinsicWidth() * 0.7), false);
Above function is called from onItemSelected. **The app still works well on 2.2 and 2.3, but throws exception immediately on 4.1 Above code works fine, but throws following exception. I didnot see any crashes in 2.2 and 2.3, but it immedidately crashes in 4.1 Is there any major difference of memory management in Jelly beans? **:
java.lang.OutOfMemoryError
AndroidRuntime(2616): at android.graphics.Bitmap.nativeCreate(Native Method)
AndroidRuntime(2616): at android.graphics.Bitmap.createBitmap(Bitmap.java:640)
AndroidRuntime(2616): at android.graphics.Bitmap.createBitmap(Bitmap.java:586)
AndroidRuntime(2616): at android.graphics.Bitmap.createScaledBitmap(Bitmap.java:466)
AndroidRuntime(2616): at com.rdx.gallery.GalleryDemoActivity.setSelectedImage(GalleryDemoActivity.java:183)
It's important to note that following code can cause Exception:
Bitmap bitmap = Bitmap.createScaledBitmap(oldBitmap, newWidth, newHeight, true);
oldBitmap.recycle();
Proper is:
Bitmap bitmap = Bitmap.createScaledBitmap(oldBitmap, newWidth, newHeight, true);
if (oldBitmap!= bitmap){
oldBitmap.recycle();
}
because documentation says:
If the specified width and height are the same as the current width
and height of the source btimap, the source bitmap is returned and now
new bitmap is created.
http://www.youtube.com/watch?v=_CruQY55HOk. After andorid 3.0 bitmaps pixel data are stored on the heap. It seems you are exceeding heap memory size. Just because your app requires large heap do not use large heap. More the size of heap, more regular garbage collections. The video has a good explanation on the topic.
Also recycle bitmaps when not in use. Garbage collections on heap is done my mark and sweep , so when you recycle bitmaps it free's memory. So your heap size will not grow and run out of memory.
bitmap.recycle();
http://developer.android.com/training/displaying-bitmaps/load-bitmap.html. Documentation on loading bitmaps efficiently. Have a look at loading scaled down version in memory.
Apart form this you can use Universal Image Loader. https://github.com/nostra13/Android-Universal-Image-Loader.
https://github.com/thest1/LazyList. Lazy Loading of Images.
Both use caching.
You are trying to access more memory then you have. Try to use
BitmapFactory.Options opts=new BitmapFactory.Options();
opts.inDither=false;
opts.inSampleSize = 8;
opts.inPurgeable=true;
opts.inInputShareable=true;
opts.inTempStorage=new byte[16 * 1024];
Bitmap.createScaledBitmap(BitmapFactory.decodeResource(getResources(), R.drawable.h1)
, 65,65, true),
Also look at below links to increase memory
http://developer.android.com/reference/android/R.styleable.html#AndroidManifestApplication_largeHeap
Detect application heap size in Android
EDIT 1
try to use nostras image downloader, you can use it to show image in local storage. And it manages memory very well ...
https://github.com/nostra13/Android-Universal-Image-Loader

Bitmap size exceeds VM budget, not understanding why

I've looked all over for "Bitmap size exceeds VM budget" problems, but none of the solutions seem applicable for me. I am not understanding why my program sometimes throws this error because they way I'm using it doesn't seem to cause any possible memory leaks. My stack traces are pointing to the BitmapFactory.decodeResource() method. I've got a background image that I'm using to draw on a Canvas and this is how I've been initializing it:
Bitmap backgroundImage = BitmapFactory.decodeResource(getResources(),
R.drawable.background);
backgroundImage = resizeImage(backgroundImage, w, h);
This is how I've been using it:
canvas.drawBitmap(backgroundImage, 0, 0, paint);
I thought that putting backgroundImage = null in the onDestroy method would help, but that did nothing. There is not other reference to the background image resource in my program except in an XML file, but I don't think that affects it. Could someone explain to me why this is happening and how to fix it?
By the way, there is not screen orientation changes involved in this app.
You need to free the bitmap pixels when you're done with it. You mentioned that you set its value to null, but that only makes it eligible for GC, it does not explicitly tell the VM that you're done with those pixels, and that now is a good time to free them.
Before you set it to null, simply call Bitmap#recycle() on the Bitmap:
protected void onDestroy() {
if (this.backgroundImage != null) {
this.backgroundImage.recycle();
this.backgroundImage = null;
}
}
Additionally, you may be wasting resources in your resizeImage() method, which you did not provide code for. It's much more efficient to do proper down-sampling of the Bitmap at decode-time, rather than loading the full-size Bitmap, and then scaling it down from there.
The general technique is to use the 3-argument version of BitmapFactory.decodeResource(), with BitmapFactory.Options#inJustDecodeBounds for a first-time-pass in order to get the width/height of the Bitmap (although in your case, since it comes from the app's resources, there's no reason you should even have to do that.. but I'll explain it anyway); then determine a proper samplesize based on the target size, and decode the bitmap a second time. That usually results in much less memory usage, especially for very large images (e.g., with inSampleSize set to 2, it decodes the full-size Bitmap, but only allocates enough memory for a Bitmap of half the original size, downscaling the Bitmap in the process).

android - rendering bitmaps from native code - nativeCreate bitmaps are not cleanedup from memory

I am streaming a video in android and I decode frames in native code and then copy the pixels to a bitmap, then display the bitmap in Java using canvas.unlockandpost with a while loop for all the bitmaps.
Everything is fine, but the streaming of bitmaps is very slow and causes a crash. I only see a message on logcat saying that "low memory no more background processes".
I see on the allocation table from eclipse, that the bitmaps that I created are not getting deleted from memory, even though, I am overwritng the pixels everytime. Is there any way I can clean up the memory it is keeping.
My code is as follows.
C Code :
AndroidBitmapInfo info;
void* pixels;
int ret;
if ((ret =AndroidBitmap_lockPixels(env, bitmap, &pixels)) < 0) {
}
memcpy(pixels, pictureRGB, 480*320);
AndroidBitmap_unlockPixels(env, bitmap);
Java Code
Bitmap mBitmap = Bitmap.createBitmap(480, 320, Bitmap.Config.RGB_565);
renderbitmap(mBitmap, 0);
canvas.drawBitmap(mBitmap, 0, 0, null);
The code shown in your question is missing some critical parts to fully understand your problem, but it sounds like you're creating a new bitmap for every frame. Since Android only allows for about 16MB of allocations for each Java VM, your app will get killed after about 52 frames. You can create a bitmap once and re-use it many times. To be more precise, you are creating a bitmap (Bitmap.CreateBitmap), but not destroying it (Bitmap.recycle). That would solve your memory leak, but still would not be the best way to handle it. Since the bitmap size doesn't change, create it once when your activity starts and re-use it throughout the life of your activity.

Categories

Resources