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.
Related
I'm curious about MemorySizeCalculator of Glide. I can get default memory cache size from getMemoryCacheSize() and bitmap pool size from getBitmapPoolSize().
MemorySizeCalculator:
/**
* Returns the recommended memory cache size for the device it is run on in bytes.
*/
public int getMemoryCacheSize() {
return memoryCacheSize;
}
/**
* Returns the recommended bitmap pool size for the device it is run on in bytes.
*/
public int getBitmapPoolSize() {
return bitmapPoolSize;
}
I understood memory cache concept as well but not sure bitmap pool size, so I found some information from this link : https://medium.com/#ali.muzaffar/performance-improvement-and-bitmap-pooling-in-android-f97b380cf965
Bitmap pooling is a simple technique (though fairly complex to
implement), that aims to reuse bitmaps instead of creating new ones
every time. To put it simply, when you need a bitmap, you check a
bitmap stack to see if there are any bitmaps available. If there are
not bitmaps available you create a new bitmap otherwise you pop a
bitmap from the stack and reuse it. Then when you are done with the
bitmap, you can put it on a stack.
I think that the bitmap which is pushed to a stack is also cached on memory. Is there anyone who can help me understand this concept?
bitmap pool cached dirty bitmap, that used to save time to create
bitmap. because we can't promise all of bitmap cached in
memoryCache.
memoryCache cached bitmap which you have used before.
Image heavy applications have to decode many images, so there will be continuous allocation and deallocation of memory in application. This results in frequent calling of the Garbage Collector (GC). And if you call the GC too many times, your application UI freezes. Glide use the Bitmap Pool Concept to load images efficiently. By using the Bitmap pool to avoid continuous allocation and deallocation of memory in your application, you reduce GC overhead, which results in a smooth-running application.
The basic principe is simple. When processing bitmap you have to verify if bitmap 2 is the same as bitmap 1. For this, you have to compare them. If it is the case you can reuse bitmap 1 as an inbitmap to reuse the same memory space to process gitmap 2 then skiping GC.
Memory cache is a memory space allocated to each application. One application cannot access the cache memory of other application.. It's size is limited.
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
I get "bitmap size exceeds VM budget", eventually with my app. So I added all of these things to help alleviate the increasing memory footprint
BitmapFactory.Options options = new BitmapFactory.Options();
options.inTempStorage = new byte[32*1024];
options.inDither=false; //Disable Dithering mode
options.inPurgeable=true; //Tell to gc that whether it needs free memory, the Bitmap can be cleared
options.inInputShareable=true; //Which kind of reference will be used to recover the Bitmap data after being clear, when it will be used in the future
options.inPreferredConfig = Bitmap.Config.RGB_565;
Drawable pulled = BitmapDrawable.createFromResourceStream(null, null, conn.getInputStream(), "galleryImage", options);
I am also using weakhashmaps, recycling, System.gc()
and all this successfully PROLONGES the crash. where a device with a 32M heap initially would have only been able to handle a few images before crashing, now it can handle a dozen, but this doesn't fix the problem. The problem is the BitMap method is leaking memory and simply will not clear.
How do I fix this? The images are of size 256x357
If you want to really make sure those Bitmaps are freed, you have to call recycle() on those Bitmaps that are no longer needed. If you need all of them, then recycle least recently used ones (and re-load them, again when needed).
You should try to use drawable.setCallback(null); when you don't need a drawable anymore, because even if you're using a WeakHashMap, they can still be attached to the context through their callback attribute.
See this
I was dealing with ImageView and i solved the memory problem using imageView.clearAnimation(); before to assign the new bitmap and now i have no memory error.
I'm developing an Android game consisting of many images as sprites.
When I load the images the following way:
public static Bitmap loadBitmap(int resId) {
return BitmapFactory.decodeResource(getResources(), resId, options);
}
everything works perfectly fine.
When I try to down-scale or up-scale the bitmap with this code:
public static Bitmap loadBitmap(int resId) {
Bitmap bitmap = BitmapFactory.decodeResource(getResources(), resId, options);
Matrix matrix = new Matrix();
matrix.postScale(0.8f, 0.8f);
Bitmap scaledBitmap = Bitmap.createBitmap(bitmap, 0, 0, bitmap.getWidth(), bitmap.getHeight(), matrix, true);
bitmap.recycle();
bitmap = null;
return scaledBitmap;
}
the application crashes with the following exception:
2211840-byte external allocation too large for this process.
Out of memory: Heap Size=4935KB, Allocated=2549KB, Bitmap Size=18463KB
VM won't let us allocate 2211840 bytes
Why is the scaling causing OutOfMemory exception? I even try to recycle the original image in order to save some space. I'm not using Bitmap.createScaledBitmap(...) intentionally, since this method does memory leaking internally (as explained in other online resources).
Thank you in advance,
Zlatko
You are probably just very close to the memory limit. It looks like you are creating a pretty large bitmap (and I'm not sure why you are making it the same size as the original bitmap). From the log, you have used 25MB of Java allocations, and 18MB of bitmap allocations, so you are basically right up against the 48MB heap limit.
Also I think it is very unlikely that createScaledBitmap() leaks. All it does is basically what you are doing here.
You should try to use the variable "inSampleSized" in the BitmapFactory.Options class. This will scale without using excess memory.
http://developer.android.com/reference/android/graphics/BitmapFactory.Options.html#inSampleSize
I guess you´re really close to the heap limit. In your function, you basically are instantiating a second Bitmap, which roughly leads to doubling your memory (Bitmaps are very large). If you´re on an OS earlier than Honeycomb, it´s also misleading to look at memory values, which are printed out somewhere. iirc, Bitmaps are held directly on the system memory heap, whereas everything other is held on the vm heap (and these are the values you see -> 2,5MB). However, the memory for Bitmap allocations also counts in for the memory heap limit. :/
I suggest you have a look at this Google I/O Session: http://www.youtube.com/watch?v=_CruQY55HOk
I think your problem can only be solved by bringing down the resolution of your Bitmap or by using some scale function, that doesn´t instantiate a new Bitmap and modifies the existing one (like the one mentioned by AmandeepGrewal).
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.