Potential Out of Memory error with multiple bitmaps - android

I had a user comment that after viewing a bunch of images in my app, it crashes (he believes that it is due to out of memory error). I have the following relevant code:
int themeID = mNav[mPos];
String icon = getThemeData(DbAdapter.KEY_ICON, themeID);
ImageView viewer = (ImageView)findViewById(R.id.viewer);
Bitmap bMap = null;
try {
bMap = getJPG(icon + ".jpg");
} catch (IOException e) {
e.printStackTrace();
}
viewer.setImageBitmap(bMap);
That gets reran as the user flips between images. From here I see that you should call recycle() on bitmaps. Do i need to call it on bMap after setting the image? Or is there some way to pull it from viwer prior to setting the next one?
According to the documentation for recycle (if I call it on bMap) it appears I don't need to use it: This is an advanced call, and normally need not be called, since the normal GC process will free up this memory when there are no more references to this bitmap.

If you need to explicitly call recycle() it probably means that you have memory leak. Calling it is almost never a solution.
Did you try to check your app for potential mmory leak?
To check it you can for example rotate your device a few times and check how the Garbage Collector behaves. You should have something like GC_... freed 211K, 71% free 300K/1024K, external 0K/0K, paused 1ms+1ms in your LogCat nearly every time you rotate. Watch for changes in this part: 300K/1024K. If you don't have memory leaks, the first part should grow and then get smaller after a few GCs. If you have a memory leak, it will grow and grow, to the point of OOM error.
Check out my other answer about a memory leak.
If you're sure you don't have a leak and you're operating on Honeycomb you can increase the heap size accessible for your app like this: android:largeHeap="true" but it's only recommended when you deal with some huuuge bitmaps or videos, so don't overuse it.

Related

Firebase Slow Image Loading

I am currently in a bit of a struggle. I have been coding a basic social media app for quite a while now, and the images are loading slowly. I am using picasso to load images from my firebase storage and it is quite slow.
When the app is started the posts that I have loaded into a recyclerview only load when I scroll over them, but they load relatively fast. As I do more stuff, switching activities etc my images will load slower and slower until they won't load and I am stuck with my placeholder image.
It is not only the recyclerviews because I have a settings activity to display a user's credentials, and I get the same slow loading image. Here is some code.
UserRef.addValueEventListener(new ValueEventListener() {
#Override
public void onDataChange(DataSnapshot dataSnapshot) {
if(dataSnapshot.hasChild("username")){
String Username = dataSnapshot.child("username").getValue().toString();
username.setText(Username);
}
if (dataSnapshot.hasChild("profileimage")){
String image = dataSnapshot.child("profileimage").getValue().toString();
Picasso.get().load(image).placeholder(R.drawable.user_circle2).into(profileImage);
}
}
The text loads instantly, no matter how much activity the user has on the app, but the images continually load slower.
Please can someone help, or suggest an alternate method for quickly displaying images, Thanks.
EDIT:
Thanks from Doug Stevenson for telling me to remove listeners, would I do it like this?
eventListener = UserRef.addValueEventListener(new ValueEventListener() {
#Override
public void onDataChange(DataSnapshot dataSnapshot) {
if(dataSnapshot.hasChild("username")){
String Username = dataSnapshot.child("username").getValue().toString();
username.setText(Username);
}
if (dataSnapshot.hasChild("profileimage")){
String image = dataSnapshot.child("profileimage").getValue().toString();
Picasso.get().load(image).placeholder(R.drawable.user_circle2).into(profileImage);
endListener();
}
}
#Override
public void onCancelled(DatabaseError databaseError) {
}
});
}
private void endListener() {
UserRef.removeEventListener(eventListener);
}
UPDATE:
I have added the remove listeners to the app.
I was debugging this on my phone, and of course I got the slow images as mentioned before, I tried this on an emulator though and the speed of the image loading stayed the same, and I could switch activities and even after I did quite a few things, the images were all loading ok when I switched back to my posts. Any ideas why???
I am also now getting the following message in the logcat:
Starting a blocking GC Alloc
05-07 19:28:20.493 15034-16164/com.sender.hp.sender I/art: Alloc concurrent mark sweep GC freed 3(72B) AllocSpace objects, 0(0B) LOS objects, 3% free, 494MB/510MB, paused 384us total 32.059ms
05-07 19:28:20.493 15034-16164/com.sender.hp.sender W/art: Throwing OutOfMemoryError "Failed to allocate a 48771084 byte allocation with 16777216 free bytes and 17MB until OOM"
I am now thinking that my images are eating up the memory, causing the slow image display and explaining why the text can still appear easily yet the images can't. I am going to try to compress the images before I add them to firebase.
While it's impossible to know for certain with the information here exactly what is causing slowness, it's almost certain that adding event listeners without removing them when they're no longer needed will cause problems over time.
If you don't remove a listener when it's no longer needed, it will continue to receive changes that occur at the location where it's listening. In your case, that also means Picasso will continue to load images. Every time you add a listener like this without removing it, all the prior listeners will all still receive results and load images. I can only imagine that repeating this many times will quickly consume the available network bandwidth on a device with a mobile data connection.
Always be sure to remove listeners that you don't need any more.

Possible bug in dalvik of Android 2.x during Bitmap allocation?

The phenomenon: First do allocation some big memory blocks in the Java side until we catche OutOfMemoryError, then free them all. Now, weird things happen: load even a small picture(e.g. width:200, height:200) by BitmapFactory.decodeXXX(decodeResource, decodeFile, ...) will throw an OutOfMemoryError! But its OK to alloc any pure Java big Object(e.g. new byte[2*1024*1024]) now!
Verifying: I wrote some simple codes to verify the problem that can download here, press "Alloc" button many times and you will got an OOF Error, then press "Free All", now the environment is set up. Now you can press "LoadBitmap" and you will see its not work on most of Android 2.x phone.(But in the emulator its just OK, odd)
Digging deeper: I try to dig into some dalvik code to find out why, and find a possible bug in function externalAllocPossible in HeapSource.c which called by dvmTrackExternalAllocation who print the "xxx-byte external allocation too large for this process" messages in LogCat.
In externalAllocPossible it simply wrote:
if (currentHeapSize + hs->externalBytesAllocated + n <=
heap->absoluteMaxSize)
{
return true;
}
return false;
Which means once if the native Bitmap allocation size plus the currentHeapSize(NOT the actually allocated size as shown below, in this case, it's keeping the max size of the heap we bumped up but then freed them all) exceeds the limits, native Bitmap allocation will always fail, but the currentHeapSize in Java seems NOT decrease even when 91.3% Java objects' memory have been freed(set to null and trigger GC)!
Is there anybody else met this problem too?
I think this is correct. Its forcing the entire app (Java+native) take no more than a certain amount of memory from the OS. To do this it has to use the current heap size, because that amount of memory is still allocated to the app (it is not returned to the OS when freed by GC, only returned to the application's memory pool).
At any rate, 2.x is long dead so they're not going to fix it there. They did change how bitmaps store their memory in 3.x and 4.x. Your best bet is to allocate all the bitmaps you use first, then allocate those large structures. Or better yet- throw those large structures into a fixed size LRUCache, and don't use the grow until out of memory idea, instead load new data only when needed.
The class Bitmap has the recycle() method, described as:
Free the native object associated with this bitmap...
The reason behind this method is that there are two heaps: the Java heap and the heap used by native code. The GC only sees the Java heap sizes; for GC, a bitmap may look as a small object because it's size on the Java heap is small, despite the fact that it references a large memory block in the native heap.

Android Heap Memory size doesn't looks correct

i have an Android app that displays alot of images, it works, the images are gatherd from an url, added to a que and gathered by 4 threads,stored in a cache and then displayed in a listview view 4 images for row, there are abot six rows at each time on the screen. There is a total of usually 90 images.
The rows(and imageviews) are always recycled, so the amount of items is always the same and i'm not initializing anything.
This seems to work quite fine, i have always an average used heap size of 13MB.
The problem i have is that at the beginning mi max heap size is quite small and i get GC messages like:
01-20 16:48:39.191: D/dalvikvm(9743): GC_FOR_ALLOC freed <1K, 31% free 12048K/17351K, paused 25ms
but the more i scroll up down the view the heap size grows more and more untile i get things like
01-20 17:02:05.339: D/dalvikvm(11730): GC_FOR_ALLOC freed 544K, 72% free 13871K/49159K, paused 35ms
as you see even if the used is the same the maximum is increased even if i never got to that limit. and the true problem is that at this point i start to get outofmemory errors.
Can someone explain me what's wrong?
Thanks!
What version of Android are you using? If you're testing on pre 3.0 (ie 2.x), the byte arrays that store most of the information in Bitmaps are allocated and stored in native memory. This means that in heap dumps and in the GC notifications, you only see the small amount of memory used for pointers in Bitmaps, rather than the actual size.
For more information check out this google IO talk on memory management and detecting memory leaks: http://www.youtube.com/watch?v=_CruQY55HOk
Also I've worked on several apps doing similar things. My guess is that either your cache size is way too large, or (more likely) the images you're displaying and storing in the cache are much larger than the size you actually want. If you display a bitmap in an image view, the imageview will store the original bitmap in memory, even if it is significantly larger than what would actually fit in the view. Try resizing the images from disk to at least closer to the appropriate size before trying to display them: How do I scale a streaming bitmap in-place without reading the whole image first?
To cache my Images I use Map<String, Drawable> drawableMap. On a OutOfMemoryError I call this function:
private void cacheLeeren()
{
int size = drawableMap.size();
int del = (int) (size * 0.3);
Set<String> s = drawableMap.keySet();
for (String t : s)
{
if (del >= 0)
{
drawableMap.put(t, null);
del--;
}
}
}
I think it's not the best way...but it works ;-)
My guess is that your app reaches a very high peak of memory usage for a short time. It's true that on average you only use 13MB but if your heap grows to as much as 50MB, it means that momentarily you've consumed much more memory than you're thinking.
Let's try to figure out where this is happening. You've mentioned that you're using an LRU cache. This cache frees memory as soon as it fills up. My guess is that you're starting to free memory too late, and this memory isn't freed immediately - since it depends on the system GC. Whenever you're freeing some items from the cache, try to call System.gc() manually.
You've also mentioned that you're calling Bitmap.recycle(). To the best of my knowledge this is useless on Android 3+ because the native heap is no longer used for bitmaps. Since all bitmaps are on the dalvik heap, they will be freed by the GC.. You can't rush this like before unless you call System.GC() yourself.
Another idea for your source of problems is heap fragmentation. See my previous SO answer to a similar issue in this question.

Android: Out of memory (VM budget...) on startup, on small percent of total installs

I get a clasical "VM budget excedees memory - out of memory" type error crash report from the Android Market.
I checked the app for memory leaks over and over again. This error happens on a very small percent of total application installs, around 1-2% and it always happens on start-up. The app loads some bitmaps from internal memory for each activity, but does not crash on most devices. I thought all applications had a guaranteed minimum stack size for bitmaps so this should work for every device. Min SDK is 7.
Any reason why ? Does this sound familiar to anyone ?
I had quite a similar problem, and my images were simply too big for some devices.
I have read that you have one image per Activity and I guess this happens when switching from one to another as the newly allocated Drawable cannot fit. What you could do, to save some memory, would be to unload the background image of the Activities that are not shown:
#Override
protected void onResume() {
super.onResume();
Drawable d = loadMyDrawableFromDisk();
setBackgroundDrawable(d);
}
#Override
protected void onPause {
setBackgroundDrawable(null);
super.onPause();
}
It may help as the memory will be freed a few after onPause() is called, and not when the underlying View of your Activity will be unallocated by the system.

Android bitmap allocation weirdness

I'm having some trouble understanding why this code
public class BitmapAllocTest extends Activity {
/** Called when the activity is first created. */
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
byte[] b = new byte[20 * 1000 * 1000];
b = null;
Bitmap.createBitmap(2500, 2000, Bitmap.Config.ARGB_8888);
}
}
throws an OutOfMemory exception on a device with a 24mb heap limit. If I comment out either of the allocations it runs fine. I was under the impression that the java vm would try to garbage collect before throwing OutOfMemory exceptions.
I suspect it having to do with android allocating the bitmaps on the native heap.
I posted this on the issue tracker and got this answer:
There are a couple of things going on.
The VM on older devices uses
conservative collection. Most (but
not all) devices running >= 2.0 will
use type-precise GC, but none of them
yet have live-precise GC.
What this means is, the fact that you
set "b = null" doesn't guarantee that
all copies of that reference are gone
-- a copy might still be sitting in a register somewhere, and without
liveness detection the GC can't know
that it will never be used again.
It's also perfectly legal for the
compiler to discard the "b = null"
assignment since you never look at "b"
again.
Bitmap pixel data uses the magical
"external allocation" mechanism rather
than the usual heap allocator.
Sometimes you get unpleasant
interactions.
We're working on fixing all of these
issues.
Link: http://code.google.com/p/android/issues/detail?id=10821
I was under the impression that the java vm would try to garbage collect before throwing OutOfMemory exceptions.
You have to trigger the GC by yourself and retry. I had to do that recently and couldn't figure out another way to do that.

Categories

Resources