I have a 1000x1500 pixel bitmap of which I want to make a mutable copy in Android.
When I run the following code...
// int width = original.getWidth(); // 1000px
// int height = original.getHeight(); // 1500px
final Bitmap result = original.copy(original.getConfig(), true);
original.recycle();
...I get an OutOfMemoryError on the copy line:
java.lang.OutOfMemoryError: bitmap size exceeds VM budget
ERROR/GraphicsJNI(419): VM won't let us allocate 6000000 bytes
Why does the copy instruction need 6MB (!) for a 1000x1500 pixel bitmap?
How can I create a mutable bitmap from a non-mutable one in more memory-efficient way?
Edit
BitmapFactory returns inmutable bitmaps. Apparently, the only way of creating a mutable bitmap from an immutable one is to copy it into a new mutable bitmap. In the case of a 1000x1500 bitmap, this apparently requires 12MB (1000x1500x4x2), which causes an OutOfMemoryError in most Android devices.
Is this problem unsolvable in Android then?
To answer your first question:
1000*1500*32/8=6000000
(32 bits/pixel for color information)
To answer your second question: you need to reduce the size of the image, either by processing it in chunks, or be reducing the resolution or color depth.
cdonner put me in the right direction.
Turns out that the original bitmap was using ARGB_8888, which requires 32 bits per pixel and is more than what was needed for this particular app.
Changing the bitmap config to RGB_565, which requires 16 bits per pixel, reduced memory consumption by half.
There is a tricky workaround which I used to avoid OutOfMemoryError.
I registered a receiver so that it ran on different process:
<receiver android:name=".ImageTransformReceiver"
android:exported="true" android:process=":imageTransformProcess"/>
Inside a receiver I do expensive memory operations (loading of two large images and merging them into one). I write the result image into file and send broadcast back to main process referring to the result image file path in Intent.
This is just a hack that allows you to use more OS memory inside one application. Hope this helps.
As of API level, 11 BitmapFactory.Options has a boolean 'inMutable' which can be set to produce mutable Bitmaps.
While this doesn't change the memory use for the individual bitmap, it will save you from having to store two copies in memory.
Related
I have wrote a code for a button to let me move from an activity in my application to my main activity. But an error occurred during run :
07-21 09:28:13.864: E/dalvikvm-heap(371): 25165824-byte external allocation too large for this process.
07-21 09:28:13.864: E/GraphicsJNI(371): VM won't let us allocate 25165824 bytes
I have used an image and stored them in my database but without any recycle is this the problem?
Here is my code in bitmap :
try {
Log.d("to get image", "ok");
ImageView imageView = (ImageView) rowView.findViewById(R.id.imageView1);
Bitmap bitmap = BitmapFactory.decodeStream((InputStream)new URL(imageInfo.get(position)).getContent());
imageView.setImageBitmap(bitmap);
} catch (MalformedURLException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
My app suppose to retrieve default images from youtube for any key search word and store them in my data base.
Android apps have a limited heap size (minimum on old devices is as little as 16MB). This varies per device, so be careful during QA. Since a bitmap can take as much as 4 bytes per pixel, large bitmaps can easily fill up your heap (1000 x 1000 pixels = 3.8MB). Your bitmap seems to take 25MB which is huge!
What this means is that you have to be very careful and use several tactics to avoid wasting memory:
When you don't need the bitmap anymore, release it and call bitmap.recycle() to make sure the memory is freed immediately. If you need the bitmap again in the future, you can load it again from disk/resources.
If the bitmap is larger than your screen size, you can scale it down to save memory. The technique here will let you load it initially reduced by 2/3/4.. If you need more fine-tuning, you can load it full size and then rescale using Bitmap.createScaledBitmap. Just don't forget to recycle() the original large bitmap immediately.
Please note that with bitmap scaling, I did not recommend to just resize your source images. Different Android devices have different resolutions. So I would keep my source images big enough to look perfect on the highest resolution devices available. Low resolution devices usually have less heap memory, so the runtime scaling takes care of those.
This error means that You have a too large image inside. The problem here is, that You get a memory leak. If You use bitmaps, the best way is too use small bitmaps that fits on multiple devices. If You use bitmaps, also call recycle() if this bitmap is not used anymore. I recommend to read through this Guide to use Bitmaps:
http://developer.android.com/training/displaying-bitmaps/load-bitmap.html
Also, there is an alternative if recycling is not possible to You. You can scale Your bitmap to Your needs like perfectly explained in another thread here in stackoverflow:
Strange out of memory issue while loading an image to a Bitmap object
This might sound like a strange/silly question. But hear me out.
Android applications are, at least on the T-Mobile G1, limited to 16
MB of heap.
And it takes 4 bytes per pixel to store an image (in Bitmap form):
public void onPictureTaken(byte[] _data, Camera _camera) {
Bitmap temp = BitmapFactory.decodeByteArray(_data, 0, _data.length);
}
So 1 image, at 6 Megapixels takes up 24MB of heap. (Cue Memory overflow).
Now I am very much aware of the ability to decode with parameters, to effectively reduce the size of the image. I even have a method which will scale it down to a desired size.
But what about in the scenario when I want to use the camera as a quality camera!
I have no idea how to get this image into the database. As soon as I decode, it errors.
Note: I need(?) to convert it to Bitmap so that I can rotate it before storing it.
So to sum it up:
Limited to 16MB of heap
Image takes up 24MB of heap
Not enough space to take and manipulate an image
This doesnt address the problem, but I Recommend it as a starting point for others who are just loading images from a location:
Displaying Bitmaps on android
I can only think of a couple of things that might help, none of them are optimal
Do your rotations server side
Store the data from the capture directly to the SDCARD without decoding it, then rotate it chunk by chunk using the file system, then send that to your DB. Lots of examples on the web (if your angles are simple, 90 180 etc) though this would be time consuming since IO operations against SDCARD's are not exactly fast.
When you decode drop the alpha channel, this may not solve you issue though and if you are using a matrix to rotate the image then you would need a target/source anyway
Options opt = new Options();
opt.inPreferredConfig = Bitmap.Config.RGB_565;
// Decode the raw camera a bitmap with no alpha channel
bmp = BitmapFactory.decodeByteArray(raw, 0,raw.length, opt);
There may be a better way to do this, but since your device is so limited in heap etc. I can't think of any.
It would be nice if there was an optional file based matrix method (which in general is what I am suggesting as option 2) or some kind of "paging" system for Android but that's the best I can come up.
First save it to the filesystem, do your operations with the file from the filesystem...
I am trying to implement the code below converting a image path into a bitmap to display on my activity. I am getting the below error. I have tried a bunch of different solutions but none are working
Strange out of memory issue while loading an image to a Bitmap object
Android: Resize a large bitmap file to scaled output file
OutOfMemoryError: bitmap size exceeds VM budget :- Android
Error:
E/dalvikvm-heap(19252): 12742656-byte external allocation too large for this process.
for(int i = 0; i < numItems; i++) {
File imgFile = new File(photoPaths.get(i));
if(imgFile.exists())
{
images[i] = BitmapFactory.decodeFile(imgFile.getAbsolutePath());
}
You may consider loading them in a more just-in-time kind of approach, or using the inSampleSize option of the bitmap factory (i.e., you'd pass a BitmapFactory.Options in to the factory with inSampleSize set to, ideally, a power of 2). Also make sure you set inPurgeable true.
In the event that you are pulling these images from the MediaStore's ContentProvider, you can also use thumbnails.
Perhaps you can tell us more about your use case so we can better help.
You're trying to load multiple large bitmaps, at least one of which is ~12MB. They're too large. The solutions you posted DO work. Resize your bitmaps to something much smaller, especially as you're just displaying the bitmaps on the screen.
This question already has answers here:
Strange OutOfMemory issue while loading an image to a Bitmap object
(44 answers)
Closed yesterday.
I am getting the following error after creating bitmap second time around:
04-17 18:28:09.310: ERROR/AndroidRuntime(3458): java.lang.OutOfMemoryError: bitmap size exceeds VM budget
this._profileBitmap = Bitmap.createBitmap(_profileBitmap, xCoor, yCoor, width, height);
From log:
04-17 18:27:57.500: INFO/CameraCropView(3458): Original Photo Size: W 1536 x H 2048
04-17 18:28:06.170: INFO/CameraCropView(3458): xCoor: 291
04-17 18:28:06.170: INFO/CameraCropView(3458): yCoor: 430
04-17 18:28:06.170: INFO/CameraCropView(3458): Width: 952
04-17 18:28:06.170: INFO/CameraCropView(3458): Height: 952
Since the image is huge I get the error. But the interesting thing is the error does not happen the first time, only when I take the picture the second time, which makes me believe this profileBitmap is NOT destroyed. How do I clean this up?
I had the same problem and fix it this way:
My app was ~18MB size, and when I see how much memory left free I was shocked - 654KB (on 1GB RAM!). So I just deleted almost all images from project and downloaded them from the internet on first start, and use pics from SD card when needed.
To check total/free memory for your app use:
Runtime.getRuntime().totalMemory();
Runtime.getRuntime().freeMemory();
EDIT: I forgot the main thing - add in your manifest, between application tag, this line:
android:largeHeap="true"
There are many problems with memory exceptions with bitmaps on Android, many of which are discussed on stackoverflow. It would probably be best if you went through the existing questions to see if yours matches one of the existing ones, then if not, write up what makes your situation different.
Some examples:
Out of memory exception due to large bitmap size
Android: out of memory exception in Gallery
Android handling out of memory exception on image processing
etc:
https://stackoverflow.com/search?q=android+out+of+memory+exception+bitmap
I have explained it in this blog post: android bitmap processing tips
Now here are tips which you can follow and can avoid out of memory exception in your Android Application.
Always use Activity context instead of Application context. because Application context cannot be garbage collected. And release resources as your activity finishes. (life cycle of object should be
same as of activity).
2 . When Activity finishes. Check HEAP DUMP (memory analysis tool in Android studio).
If there are objects in HEAP DUMP from finished activity there is memory leak. review your
code and identify what is causing memory leak.
Always use inSampleSize
Now what is inSampleSize ?
with the help of inSampleSize you are actually telling the decoder not to grab every pixel in memory, instead sub sample image.
This will cause less number of pixels to be loaded in memory than the original image. you can tell decoder to grab every 4th pixel or every second pixel from original image.
if inSampleSize is 4. decoder will return an Image that is 1/16 the number of pixels in original image.
so how much memory you have saved ? calculate :)
Read Bitmap Dimensions before loading into memory.
How reading bitmap dimensions before loading image into memory can help you avoid out of
memory error ? Let's Learn
use inJustBounds = true
here is technique with the help of which you can get image dimension beore loading it in memory
BitmapFactory.Options options = new BitmapFactory.Options();
options.inJustDecodeBounds = true;
Bitmap bitmap = BitmapFactory.decodeResource(getResources(), R.id.myimage, options);
int imageHeight = options.outHeight;
int imageWidth = options.outWidth;
String imageType = options.outMimeType;
Above code snippet will not give us any image/bitmap. it will return null for bitmap Object.
but it will definitely return width and height of that image. which is R.id.myimage.
Now you have width and height of Image. you can scale up or scale down image based on these factors:
ImageView size which will be used to display Image.
Available amount of memory. you can check available amount of memory using ActivityManager and getMemoryClass.
Screen size and density of device.
Use appropriate Bitmap Configuration
Bitmap configurations is color space/color depth of an Image. Default bitmap Configuration in Android is RGB_8888 which is 4 bytes per pixel.
If you use RGB_565 color channel which use 2 Bytes per pixel. half the memory allocation for same resolution :)
Use inBitmap property for recycling purpose.
Do not make static Drawable Object as it cannot be garbage collected.
Request large heap in in manifest file.
Use multiple processes if you are doing lot of image processing(memory intensive task) or use NDK (Native Development using c, c++)
You can try calling recycle() on the bitmap when you are done with it. This will clear all the image data and free up the memory. If anything tries to draw the bitmap after this then your app will crash. If you do get a crash it may help you find out what is still holding onto your bitmap.
This happens because you are loading the bitmap directly,which consumes a lot of memory.
Instead use a scaled down version of the picture in _profileBitmap.
This guy explains it pretty well.
http://androidcocktail.blogspot.in/2012/05/solving-bitmap-size-exceeds-vm-budget.html
With Larger images it can be avoided by sampling them into smaller size.
Use below example -
File f = new File(selectedImagePath);
// First decode with inJustDecodeBounds=true to check dimensions
final BitmapFactory.Options options = new BitmapFactory.Options();
options.inJustDecodeBounds = true;
BitmapFactory.decodeStream(new FileInputStream(f), null, options);
// Calculate inSampleSize
options.inSampleSize = calculateInSampleSize(options, 720, 1280); //My device pixel resolution
// Decode bitmap with inSampleSize set
options.inJustDecodeBounds = false;
Bitmap bmpPic = BitmapFactory.decodeStream(new FileInputStream(f), null, options);
Bitmap bmpPic1 = Bitmap.createBitmap(bmpPic, 0, 0, bmpPic.getWidth(), bmpPic.getHeight(), mat, true);
img.setImageBitmap(bmpPic1); //img is your ImageView
Reference-
http://developer.android.com/training/displaying-bitmaps/load-bitmap.html
You could use a vector Drawable . It uses an xml file to describe your image , so it consumes less memory.
To do that you should use the SVG format for your images and then generate the xml file using one of these 2 solutions :
Solution 1 : Use the vector asset studio in Android Studio : right click on Drawable file in your project -> new -> vector asset
Solution 2 : Use the svg2android website : https://inloop.github.io/svg2android
Check out this link for further information:
https://developer.android.com/studio/write/vector-asset-studio.html
I had the same issue when the phone was powered off and back on. Simply setting the bitmaps to null and calling System.gc(); fixed all the problems.
I had this issue because I was modifying a bitmap once, and then modifying the modified version a second time, resulting in three versions of the same bitmap (original, plus the two modified versions) being in memory at the same time.
I fixed it by changing my image-editing code to apply both modifications to the same bitmap as a kind of batch process, halving the number of modified versions that my app had to hold in memory.
Let's say I have loaded an image in a bitmap object like
Bitmap myBitmap = BitmapFactory.decodeFile(myFile);
Now, what will happen if I load another bitmap like
myBitmap = BitmapFactory.decodeFile(myFile2);
What happens to the first myBitmap? Does it get Garbage Collected or do I have to manually garbage collect it before loading another bitmap, eg. myBitmap.recycle()?
Also, is there a better way to load large images and display them one after another while recycling on the way?
The first bitmap is not garbage collected when you decode the second one. Garbage Collector will do it later whenever it decides. If you want to free memory ASAP you should call recycle() just before decoding the second bitmap.
If you want to load really big image you should resample it. Here's an example: Strange out of memory issue while loading an image to a Bitmap object.
I think the problem is this: On pre-Honeycomb versions of Android, the actual raw bitmap data is not stored in VM memory but in native memory instead. This native memory is freed when the corresponding java Bitmap object is GC'd.
However, when you run out of native memory, the dalvik GC isn't triggered, so it is possible that your app uses very little of the java memory, so the dalvik GC is never invoked, yet it uses tons of native memory for bitmaps which eventually causes an OOM error.
At least that's my guess. Thankfully in Honeycomb and later, all bitmap data is stored in the VM so you shouldn't have to use recycle() at all. But for the millions of 2.3 users (fragmentation shakes fist), you should use recycle() wherever possible (a massive hassle). Or alternatively you may be able to invoke the GC instead.
You will need to call myBitmap.recycle() before loading the next image.
Depending on the source of your myFile (E.g. if it is something you have no control over the original size), when loading an image instead of just simply resampling some arbitrary number, you should scale the image to the display size.
if (myBitmap != null) {
myBitmap.recycle();
myBitmap = null;
}
Bitmap original = BitmapFactory.decodeFile(myFile);
myBitmap = Bitmap.createScaledBitmap(original, displayWidth, displayHeight, true);
if (original != myBitmap)
original.recycle();
original = null;
I cache the displayWidth & displayHeight in a static that I initialized at the start of my Activity.
Display display = getWindowManager().getDefaultDisplay();
displayWidth = display.getWidth();
displayHeight = display.getHeight();
Once bitmap had been loaded in memory , in fact it was made by two part data.
First part include some information about bitmap , another part include information about pixels of bitmap( it is maked up by byte array).
First part exisits in Java used memory, second part exisits in C++ used memory. It can use each other's memory directly.
Bitmap.recycle() is used to free the memory of C++.
If you only do that,the GC will collection the part of java and the memory of C is always used.
Timmmm was right.
according to :
http://developer.android.com/training/displaying-bitmaps/cache-bitmap.html
In addition, prior to Android 3.0 (API Level 11), the backing data of a bitmap was stored in native memory which is not released in a predictable manner, potentially causing an application to briefly exceed its memory limits and crash.