Android IMAPFolder.fetch OutOfMemoryError: what can I do about that? - android

EDIT:
I do not see this OOM error anymore. This was possibly related to some memory leak somewhere else in the app: I found one possible Activity leak: this could be the culprit and JavaMail has nothing to do with this activity leak... my mistake.
My simplified mail app is working fine for more than 1 year, but lately, I started to receive some bug report about OutOfMemoryError occurring on Samsung SM-T530 matissewifi 5.0.2 device (i.e. Galaxy Tab 4 (10.1'',Wi-Fi) ).
Code excerpt:
try
{
FlagTerm ft = new FlagTerm(new Flags(Flags.Flag.SEEN), false);
Message[] messages = folder.search(ft);
folder.fetch(messages, createFetchProfile());
return messages;
}
catch (Throwable th)
{
// I got OutOfMemoryError here because of folder.fetch(...):
/*
java.lang.OutOfMemoryError: Failed to allocate a 1036 byte allocation with 8388608 free bytes and 387MB until OOM; failed due to fragmentation (required continguous free 131072 bytes for a new buffer where largest contiguous free 65536 bytes)
or even this (with !0! for largest contiguous free bytes value!!!)
java.lang.OutOfMemoryError: Failed to allocate a 1036 byte allocation with 8388608 free bytes and 385MB until OOM; failed due to fragmentation (required continguous free 131072 bytes for a new buffer where largest contiguous free 0 bytes)
at com.sun.mail.iap.ByteArray.grow(SourceFile:161)
at com.sun.mail.iap.ResponseInputStream.readResponse(SourceFile:125)
at com.sun.mail.iap.Response.(SourceFile:121)
at com.sun.mail.imap.protocol.IMAPResponse.(SourceFile:66)
at com.sun.mail.imap.protocol.IMAPProtocol.readResponse(SourceFile:458)
at com.sun.mail.iap.Protocol.command(SourceFile:414)
at com.sun.mail.imap.protocol.IMAPProtocol.fetch(SourceFile:2440)
at com.sun.mail.imap.protocol.IMAPProtocol.fetch(SourceFile:2422)
at com.sun.mail.imap.IMAPFolder.fetch(SourceFile:1417)
at <mycode...>
*/
}
fetchprofile is
FetchProfile fetchProfile = new FetchProfile();
fetchProfile.add(UIDFolder.FetchProfileItem.ENVELOPE);
// fetch other info to speed up process
fetchProfile.add(UIDFolder.FetchProfileItem.FLAGS);
fetchProfile.add(UIDFolder.FetchProfileItem.UID);
fetchProfile.add(UIDFolder.FetchProfileItem.CONTENT_INFO);
fetchProfile.add(UIDFolder.FetchProfileItem.SIZE); // not sure about this one
I found grow() method source: here it is (quite simple):
public void grow(int incr)
{
byte[] nbuf = new byte[bytes.length + incr];
System.arraycopy(bytes, 0, nbuf, 0, bytes.length);
bytes = nbuf;
}
Manifest:
I already have
<application android:largeHeap="true">
in AndroidManifest.xml.
Questions:
Could this be related to a mail box containing so many email that mail API crash with OOM exception? I only fetch ENVELOPPE (and a few other things) , so I suppose this is not the correct explanation.
If, as I think, it's NOT related to the amount of email, what can I do about that?
Also, How should I interpret OOM message with value 0 for largest contiguous bytes!?! (sometimes happens, but not always)
java.lang.OutOfMemoryError: Failed to allocate a 1036 byte allocation with 8388608 free bytes and 385MB until OOM; failed due to fragmentation (required continguous free 131072 bytes for a new buffer where largest contiguous free 0 bytes)

Of course you should make sure that you're closing Folders when you're done with them, and not keeping references to Folders or Messages that you're no longer using. And make sure all the other parts of your application aren't causing memory leaks.
The Folder.fetch response needs to be kept in a single byte array. You can see how the byte array grows. Compared to the actual content of messages, the fetch information is usually quite small. But if you have thousands and thousands of messages, it could become large. Since you're only looking for unseen messages, it seems unlikely that that's the case.
I don't know what's possible on Android but if you could get a heap dump when this happens and examine it to determine which objects are using all the memory that would be most helpful.

Related

Codename one handling image memory size?

My Senario is the following , I have a form where I need the user to attach images that he takes from the camera [count vary depending on forms -I have many cases- ]
the application display an alert of no available memory [ on some devices , I have seen it on (Samsung A5 [2015 release] , Xiaomi Note 4)
Exception: java.lang.OutOfMemoryError - Failed to allocate a 16894 byte allocation with 11344 free bytes and 11KB until OOM
and some times on loading the map
Process: com.x.y, PID: 17227
java.lang.OutOfMemoryError: Failed to allocate a 65548 byte allocation with 14824 free bytes and 10KB until OOM
at com.android.okhttp.okio.Segment.<init>(Segment.java:62)
at com.android.okhttp.okio.SegmentPool.take(SegmentPool.java:46)
at com.android.okhttp.okio.Buffer.writableSegment(Buffer.java:1114)
at com.android.okhttp.okio.Buffer.write(Buffer.java:940)
at com.android.okhttp.okio.RealBufferedSink$1.write(RealBufferedSink.java:197)
at java.io.DataOutputStream.write(DataOutputStream.java:107)
at java.io.FilterOutputStream.write(FilterOutputStream.java:97)
at ez.a(:com.google.android.gms.dynamite_mapsdynamite#13280047#13.2.80 (040308-211705629):15)
at ez.a(:com.google.android.gms.dynamite_mapsdynamite#13280047#13.2.80 (040308-211705629):83)
at ep.b(:com.google.android.gms.dynamite_mapsdynamite#13280047#13.2.80 (040308-211705629):2)
at eo.a(:com.google.android.gms.dynamite_mapsdynamite#13280047#13.2.80 (040308-211705629):4)
at eq.a(:com.google.android.gms.dynamite_mapsdynamite#13280047#13.2.80 (040308-211705629):55)
at com.google.maps.api.android.lib6.drd.ap.a(:com.google.android.gms.dynamite_mapsdynamite#13280047#13.2.80 (040308-211705629):11)
at dx.a(:com.google.android.gms.dynamite_mapsdynamite#13280047#13.2.80 (040308-211705629):17)
at dx.run(:com.google.android.gms.dynamite_mapsdynamite#13280047#13.2.80 (040308-211705629):65)
the code that is used to capture and take the image and store it array of images
String filePath = Capture.capturePhoto();
applicationAttachment[index] = temp;
applicationAttachment[index].setImageName(labelName.substring(0, labelName.length()-"Label".length()));
My question here , how can I handle the image based on available memory if that is possible ??

Heap not expanding on Genymotion emulator

While resizing large bitmaps for faster image upload to a server I occasionally ran into OutOfMemoryErrors.
To prevent this I calculate the required amount of memory and check if it exceeds Runtime.getRuntime().maxMemory() before trying to scale an image.
However, I still run into OOM errors even though the image should fit on the heap easily.
The emulated device (Galaxy SII API 16) gives me a max memory of 67108864 bytes using the above method.
In the following snippet, the heap size is 43975K and only < 15K of that memory is in use. For my ~31K allocation the heap should grow automatically to about 45K which is still not even close to the maximum size of 64 MiB.
But as you can see, instead of expanding the heap, the dalvik vm runs out of memory.
10-13 20:35:57.223: D/dalvikvm(1201): GC_FOR_ALLOC freed 505K, 67% free 14692K/43975K, paused 31ms, total 31ms
10-13 20:35:57.223: I/dalvikvm-heap(1201): Forcing collection of SoftReferences for 31961100-byte allocation
10-13 20:35:57.251: D/dalvikvm(1201): GC_BEFORE_OOM freed 2K, 67% free 14689K/43975K, paused 29ms, total 29ms
10-13 20:35:57.251: E/dalvikvm-heap(1201): Out of memory on a 31961100-byte allocation.
I wonder if this can happen on a real device too or if this could be a genymotion bug.
Is the heap guaranteed to expand up to maxMemory()? The JavaDoc for Runtime.getRuntime().freeMemory() says it "may" expand, whatever that means.
I just need a realiable way to calculate the amount of memory I can use, this is how I did it, please correct me if I'm wrong:
long maxMemory = Runtime.getRuntime().maxMemory();
long usedMemory = Runtime.getRuntime().totalMemory() - Runtime.getRuntime().freeMemory();
long availableMemory = maxMemory - usedMemory;
This call causes the OutOfMemoryError:
// outOptions has an appropriate inSampleSize
BitmapFactory.decodeStream(inputStream, null, outOptions);
Out of memory on a 31961100-byte allocation
Your bitmap is 32M. VM can't allocate 32M linear space to store bitmap. Heap is fragmented, so even if your heap has 32M free space it is not always possible to allocate such linear space. You can try free up as much memory as you can and call GC before decoding stream.
Try to decode your bitmap in more effective way. Or process image in parts.
If you tell us why you need this image, we can tell you how to handle it.
You have a 42MB heap , out of which 14MB is already used, 67% (28M) is free/available
D/dalvikvm(1201): GC_BEFORE_OOM freed 2K, 67% free 14689K/43975K, paused ...
E/dalvikvm-heap(1201): Out of memory on a 31961100-byte allocation.
You are trying to allocate ~31M (not 31K) , which is greater than 28M that is available, resulting in OOM.
For details on interpreting dalvikvm memory allocation log message take a look at debugging memory
There is lot of shared memory usage going on in android, to properly calculate per process memory usage refer this SO question
Android best practices on efficient bitmap memory management may be of help
One thing you might try to tweak is build.props file of the ROM.
On Genymotion emulator you can try executing the following via root shell:
cat /system/build.prop | grep dalvik
and it would display the line with dalvik settings:
dalvik.vm.heapsize=256m
dalvik.vm.lockprof.threshold=500
dalvik.vm.stack-trace-file=/data/anr/traces.txt
And maxmemory is also being reported as 268435456 bytes on the emulator I experimented with.
So, you may try playing with this setting. Also, ensure that the memory allocated in VirtualBox's settings is compatible with these values.

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.

How to measure VRAM consumption on Android?

I want to acquire怀Android Device VRAM size.
Is there a method for acquisition from the program?
Let's do some calculation using Nexus One:
Screen resolution is 480x800. So minimum required video memory size would be:
400 * 800 * 4 bytes = 1536000 bytes
Assuming that driver may (and normally should) use several buffers, we should also expect values like:
1536000 * 2 bytes = 3072000 bytes
1536000 * 3 bytes = 4608000 bytes
etc...
It would be weird to have values that are not multiple of 1536000 (or W x H x 4 in general).
After some searches on Android internals I've found this documentation :
...Android makes two requirements of the driver: a linear address space of mappable memory that it can write to directly...accessing the driver by calling open on /dev/fb0...
So I tried and take size of /dev/graphics/fb0 file (on my device there is no /dev/fb0).
But a direct approach doesn't work:
File file = new File("/dev/graphics/fb0");
file.length(); // ==0, doesn't work, no read access
Using next trick you can get actual size of fb0:
>adb pull /dev/graphics/fb0
1659 KB/s (4608000 bytes in 2.712s)
Video memory is ~4mb (Nexus One). Let's check if this is multiple of Nexus screen size:
4608000/1536000 = 3
It looks like a right value. And we also can say that driver uses three screen buffers.
So, as a conclusion, you can detect video memory size using adb, but you can't use this approach from your android application in runtime due to file access restrictions.
You typically do not have a dedicated "VRAM" on mobile devices. At least you don't have it with PowerVR architectures (wich totally dominate the market with their MBX and SGX cores).
That is, the OpenGL driver allocates normal RAM until you run out of it, and the more you allocate the less you have left for your application.
The Android/OpenGL APIs don't offer explicit methods to read the VRAM size from a given device.
Poor man solution:
You could try to infer the VRAM size in an empiric way adding 1MB texture until you get an out of memory error from gl.glGetError().
From your "dmesg" output u can read off the VRAM, so for my Tablet:
> [ 0.000000] Machine: TDM3730 [ 0.000000] Reserving 12582912
> bytes SDRAM for VRAM
>
> 7>[ 3.929962] VRAM: checking region 9f400000 3072
> <4>[ 3.929992] Failed. Allocating 4194304 bytes for fb 0
> <7>[ 3.935333] VRAM: alloc mem type 0 size 4194304 paddr dec2bd4c
> <7>[ 3.935485] VRAM: checking region 9f400000 3072
> <7>[ 3.935485] VRAM: found 9f400000, end a0000000
> <6>[ 3.936584] android_usb gadget: high speed config #1: android
> <4>[ 3.960113] allocating 4194304 bytes for fb 1
or details at:
http://pastebin.com/jQSXQqHh
Is simple just count how many Mb ram that from usable to real capacity of the ram, example for my lenovo a369i has 512 RAM Module, but in setting app only showing 471 Mb usable so the 41Mb left is reserved for the GPU, so the conclusion is my a369i has 41Mb vram
This method is based from shared graphics memory (wiki)
I suspect that android.os.StatFs is what you're looking for:
http://developer.android.com/reference/android/os/StatFs.html

Categories

Resources