In a thread i'm fetching a jpg image (from a socket) and putting it in a byte array datas[].
Then i make a bitmap and send it to my ui to display it in an imageview :
Thread side :
bundle.putParcelable("image", BitmapFactory.decodeByteArray(this.datas, 0, jpgSize,this.bitmapOption));
message.what = ThreadMessages.MSG_IMAGE_AVAILABLE;
message.setData(bundle);
this.uiHandler.sendMessage(message);
The bitmap option is defined as following :
this.bitmapOption = new BitmapFactory.Options();
this.bitmapOption.inPurgeable = true;
ui side :
cameraView.setImageBitmap((Bitmap) bundle.getParcelable("image"));
As far i know it's works fine on phone with android 2.x and 4.0.x. But i have made some test with a galaxyTab 8.9 (android 3.1) and my activitie is killed by the system due to a high memory usage :
01-30 14:12:02.311: I/ActivityManager(72): Process my.package (pid 1175) has died.
01-30 14:12:02.321: I/WindowManager(72): WIN DEATH: Window{41140848 my.package/my.package.DisplayOneCam paused=false}
01-30 14:12:02.331: I/WindowManager(72): WIN DEATH: Window{410f5020 my.package/my.package.main paused=false}
01-30 14:12:02.331: I/WindowManager(72): WIN DEATH: Window{41033308 my.package/my.package.DisplayCameras paused=false}
01-30 14:12:02.371: I/ActivityManager(72): Start proc my.package for activity my.package/.DisplayCameras: pid=1209 uid=10036 gids={3003, 1015}
01-30 14:12:02.371: I/ActivityManager(72): Low Memory: No more background processes.
01-30 14:12:02.391: I/WindowManager(72): WINDOW DIED Window{410f5020 my.package/my.package.main paused=false}
The fetched bitmap is not a very high res pictures (800*600 max) but it is displayed at least at 10 fps (more if it's possible).
Using the inPurgeable option lets me avoid the OOM crash (i had it before using inPurgeable) but it not seem to be enough .
Using inSampleSize (like explained in Strange out of memory issues) don't seems to be a solution because it resize the image, and i need a image which take the entire space of the imageview.
How can i improve my memory usage knowing that i'm receiving a jpg in a byte array and i need to display it in a imageview ?
Note : i don't need/want to resize the bitmap, the received jpg is already well sized.
Note 2 : Triggering system.gc() after each decodeByteArray seems to solve th eproblem, but it's ugly and performance are slowing down drasticly
Using Parcelable to send large amounts of data in Intents is a bad idea and is likely to result in poor performance. You would be better to store the image to persistent storage, and pass a URI to the stored image instead of the image data itself.
There is some more info on this in how do you pass images (bitmaps) between android activities using bundles?
There is no need to use a Bundle if you're only passing the Message within your application process. Simply pass your Bitmap in Message.obj.
Related
Summary
I work on an Android app which, as one of its features, involves users marking up an image and saving it. This process involves some heavy canvas drawImage() calls (sometimes on an image around 12 MB+ uncompressed) as well as lots of encoding and decoding of data. The image data also cached in the Android file system and passed from Java to JavaScript through a JavaScriptInterface.
However, with an LG G Pad X8.3 running Android 5.0.2, we are getting lots of crashing. When it crashes, the log says "WIN DEATH" and it goes back to the home screen without showing any "Unfortunately, ___ has stopped" message. Our "WIN DEATH" is preceded by a win death of the InputMethod and before that a "WIN DEATH" of com.lge.launcher2.Launcher. It is intermittent, but frequent enough to be a big problem.
Details
(1) It seems to happen after the heavy image saving manipulation, but not immediately after
(2) It happens more often when the keyboard is brought up. Is something about bringing up the keyboard causing it to crash?
(3) When watching the memory usage in Android Studio as the app is running, nothing is too out of the ordinary. When it crashes, there is still free memory available in the graph.
(3) We tried using largeHeap in our manifest, but that did not eliminate the crashing.
(4) Memory usage is between 10 and 20 MB.
(5) I can add around 32 million numbers to a JavaScript array before the web view crashes. That's at least 240 MB (8 bytes per JS number). When it crashes, it shows a blank screen, as opposed to the crash I am trying to fix where it brings you back to the home screen.
(6) There is a well publicized memory leak (or a couple, actually) in this version of Android, but LG does not offer upgrades for this model.
(7) We have tried optimizing the code to use as little memory as possible, but the crashing remains.
(8) The crash logs always contain similar information around the crash.
02-08 12:13:01.642 1850-1850/? I/PhoneApp﹕ onTrimMemory: 5
02-08 12:13:01.642 1850-1850/? I/PhoneApp﹕ trim memory
02-08 12:13:01.652 945-965/? I/ActivityManager﹕ Process com.google.android.partnersetup (pid 23908) has died
02-08 12:13:01.682 945-16584/? I/ActivityManager﹕ Process com.google.android.apps.plus (pid 23756) has died
02-08 12:13:01.692 1850-1850/? I/PhoneApp﹕ onTrimMemory: 10
02-08 12:13:01.692 1850-1850/? I/PhoneApp﹕ trim memory
02-08 12:13:01.712 945-2088/? I/ActivityManager﹕ Process com.lge.p2p (pid 24102) has died
02-08 12:13:01.742 945-2041/? I/ActivityManager﹕ Process com.google.android.gms.wearable (pid 23833) has died
02-08 12:13:01.752 1850-1850/? I/PhoneApp﹕ onTrimMemory: 15
...
02-08 12:11:06.862 22936-22936/com.mycompany.ourapp W/IInputConnectionWrapper﹕ getTextBeforeCursor on inactive InputConnection
...
02-08 12:11:06.912 20890-20890/? D/Cliptray Manager﹕ isAvailable() UserHandle.myUserId() = 0, isOwner = true
02-08 12:11:06.912 1961-8134/? I/Cliptray Service﹕ Standard mode!! ClipTray is Supported!
02-08 12:11:06.912 1961-8134/? D/Cliptray Service﹕ isAvailable() mLastIsOwner = true
02-08 12:11:06.912 1961-8134/? I/Cliptray Service﹕ Owner!! ClipTray is Supported! mIsOwnerClipTray = true
02-08 12:11:06.912 1961-8134/? D/Cliptray Service﹕ ignore packageName : com.mycompany.ourapp
...
02-08 12:12:39.782 22936-22936/com.mycompany.ourapp I/Choreographer﹕ Skipped 35 frames! The application may be doing too much work on its main thread.
...
02-08 12:13:02.622 945-2086/? I/WindowState﹕ WIN DEATH: Window{8dce4a3 u0 com.lge.launcher2/com.lge.launcher2.Launcher}
...
02-08 12:13:02.872 945-1922/? I/WindowState﹕ WIN DEATH: Window{393167d2 u0 InputMethod}
...
02-08 12:13:03.052 1979-1979/? D/QC_RIL_OEM_HOOK﹕ The connection to the service got disconnected unexpectedly!
02-08 12:13:03.052 1961-1961/? D/QC_RIL_OEM_HOOK﹕ The connection to the service got disconnected unexpectedly!
02-08 12:13:03.052 1850-1850/? D/QC_RIL_OEM_HOOK﹕ The connection to the service got disconnected unexpectedly!
02-08 12:13:03.052 1850-1850/? D/QC_RIL_OEM_HOOK﹕ The connection to the service got disconnected unexpectedly!
02-08 12:13:03.072 945-945/? W/InputMethodManagerService﹕ Session failed to close due to remote exception
android.os.DeadObjectException
at android.os.BinderProxy.transactNative(Native Method)
at android.os.BinderProxy.transact(Binder.java:496)
at com.android.internal.view.IInputMethodSession$Stub$Proxy.finishSession(IInputMethodSession.java:305)
at com.android.server.InputMethodManagerService.finishSessionLocked(InputMethodManagerService.java:1463)
at com.android.server.InputMethodManagerService.clearClientSessionLocked(InputMethodManagerService.java:1454)
at com.android.server.InputMethodManagerService.clearCurMethodLocked(InputMethodManagerService.java:1480)
at com.android.server.InputMethodManagerService.onServiceDisconnected(InputMethodManagerService.java:1499)
at android.app.LoadedApk$ServiceDispatcher.doDeath(LoadedApk.java:1391)
at android.app.LoadedApk$ServiceDispatcher$RunConnection.run(LoadedApk.java:1405)
at android.os.Handler.handleCallback(Handler.java:739)
at android.os.Handler.dispatchMessage(Handler.java:95)
at android.os.Looper.loop(Looper.java:135)
at com.android.server.SystemServer.run(SystemServer.java:302)
at com.android.server.SystemServer.main(SystemServer.java:203)
at java.lang.reflect.Method.invoke(Native Method)
at java.lang.reflect.Method.invoke(Method.java:372)
at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:908)
at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:703)
...
02-08 12:13:03.132 945-2088/? I/ActivityManager﹕ Process com.google.process.gapps (pid 21317) has died
...
02-08 12:13:03.182 1405-1405/? D/LGKeyguardUnlockMethodController﹕ onTrustChanged with userId : 0 , getUserTrustIsManaged : false ,getUserHasTrust : false
...
02-08 12:13:03.452 362-1705/? V/AudioFlinger﹕ 22936 died, releasing its sessions
02-08 12:13:03.452 362-1705/? V/AudioFlinger﹕ pid 1850 # 0
02-08 12:13:03.452 362-1705/? V/AudioFlinger﹕ pid 22936 # 1
02-08 12:13:03.452 362-1705/? V/AudioFlinger﹕ removing entry for pid 22936 session 520
02-08 12:13:03.452 362-1705/? V/AudioFlinger﹕ purging stale effects
02-08 12:13:03.452 362-1705/? V/AudioFlinger﹕ remove track (4097) and delete from mixer
02-08 12:13:03.452 945-2086/? I/WindowState﹕ WIN DEATH: Window{3eafa2d1 u0 com.mycompany.ourapp/com.mycompany.ourapp.MainActivity}
The memory leak that may be partially responsible: https://code.google.com/p/android/issues/detail?id=79729
Question
What could be causing the app to crash in this way? Is it a memory issue? is anything in the log output that I've copied here relevant?
Has anyone else had this problem and solved it? Is the Android 5.0.x/1.x memory leak bug responsible?
I wish I could be more specific, but the code is proprietary and the problem isn't (or hasn't yet been) localized to any one particular code sample. If more information is needed about a particular issue, please ask.
Update
I've noticed that there are a bunch of Choreographer messages in the logs about it doing too much work, but they are relatively small frame skips (~ 35 frames) and the last message occurs a full 20 seconds before the app crashed.
Update 2-10-16
We are now trying to reuse canvas objects in case they are not garbage collected, and we are also calling System.gc() when onTrimMemory is called with TRIM_MEMORY_RUNNING_LOW. This seems to have reduced the crashing to some degree, but still frequently enough to be concerning. Most of all, I would like to understand why it seems to randomly use up too much memory and crash. I've added the onTrimMemory lines to the log output below, which show it going from low to critical in a span of 100 ms. If this only happened when actually performing the memory-intensive actions, that would make sense, but it happens sometimes long after the user process has finished.
Pulling my hair out over here, trying to get a HTML Canvas based application running in CrossWalk on a Fire TV Stick. The app would run fine then crash after 30 minutes of use with this error. I was able to fix it but my use case is pretty specific, either way it may help folks who show up here later.
TL;DR: Reduce the size of textures being loaded into and out of the GPU.
I had a fullscreen canvas element running PixiJS, working essentially as a slideshow. The problem came from a single slide type, where large images with transparency would animate over a fixed background. After logging the CPU/GPU usage in Chrome's timeline feature I could see that the textures were being loaded into and out of the GPU all the time to accomplish the affect. Ultimately, I was able to resolve the issue by completely removing the background image element from canvas, and just displaying it in a div behind the canvas. The affect was identical, but the performance was improved significantly and this problem disappeared afterwards.
You are dealing with large bitmaps and loading all of them at run
time. You have to deal very carefully with large bitmaps by loading
the size that you need not the whole bitmap at once and then do
scaling.
Huge JavaScript Cause This
Given that you are working with limited memory, ideally you only want to load a lower resolution version in memory. The lower resolution version should match the size of the UI component that displays it. An image with a higher resolution does not provide any visible benefit, but still takes up precious memory and incurs additional performance overhead due to additional on the fly scaling.
Please check Managing Bitmap Memory.
You are using lots of canvas at once that's why this problem .
This used to happen with me, even after i used the large heap attribute in application manifest and it stopped to happen when i reset all the variable to null after finishing using them.
I don`t know your code but i would advise you to re-use the same variables and make sure you clean up after every operation and when done, because keeping in the memory large unused files is not good.
Note: I am using Android Studio, compiling the C code with the NDK outside of Android Studio.
The following C code:
jintArray Java_com_xxx_yyy_MainActivity_processFrame(
JNIEnv* env, jobject thiz, jbyteArray inArray)
{
int i;
jsize inLength = (*env)->GetArrayLength(env, inArray);
int outLength = inLength/3*2; // YYYYUV
jbyte in[outLength];
(*env)->GetByteArrayRegion(env, inArray, 0, outLength, in);
jint out[outLength];
jintArray outArray = (*env)->NewIntArray(env, outLength);
int alpha = 255 << 24;
for (i=0;i<outLength;i++) {
// just use the Y to set the B for now
out[i]=alpha | in[i];
}
// No release needed since using GetXXXRegion
//(*env)->ReleaseByteArrayElements(env, inArray, in, JNI_ABORT);
(*env)->SetIntArrayRegion(env, outArray, 0, outLength, out);
return outArray;
}
is called from Java as follows:
System.out.println("Calling processFrame");
int[] rgb = processFrame(imageData);
System.out.println("Call completed");
Yet, I do see "Calling processFrame" on my console but do not get "Call completed." Instead, I get
07-14 15:41:20.219 25810-25945/com.xxx.yyy A/libc﹕ Fatal signal 11 (SIGSEGV) at 0x5ef78000 (code=2), thread 25945 (CAMERA_THREAD)
07-14 15:41:20.219 25810-25814/com.xxx.yyy D/dalvikvm﹕ GC_CONCURRENT freed 1K, 6% free 8579K/9048K, paused 2ms+1ms, total 13ms
07-14 15:41:20.299 128-415/? W/CameraService﹕ Disconnecting camera client 0x427b09a8 since the binder for it died (this pid 128)
07-14 15:41:20.299 493-784/? I/WindowState﹕ WIN DEATH: Window{413fb7d0 u0 com.xxx.yyy/com.xxx.yyy.MainActivity}
07-14 15:41:20.299 493-493/? I/ActivityManager﹕ Process com.xxx.yyy (pid 25810) has died.
07-14 15:41:20.299 493-784/? W/WindowManager﹕ Force-removing child win Window{41685140 u0 SurfaceView} from container Window{413fb7d0 u0 com.xxx.yyy/com.xxx.yyy.MainActivity}
07-14 15:41:20.299 493-493/? W/ActivityManager﹕ Force removing ActivityRecord{41397308 u0 com.xxx.yyy/.MainActivity}: app died, no saved state
07-14 15:41:20.309 493-493/? D/WindowManager﹕ adjustConfigurationLw, config:{1.0 ?mcc?mnc ?locale ?layoutDir sw800dp w1280dp h752dp 240dpi xlrg land ?uimode ?night finger -keyb/v/h -nav/v} mLidState:-1 mHasDockFeature:true mHasKeyboardFeature:true mHasHallSensorFeature:true config.hardKeyboardHidden:2
07-14 15:41:20.309 493-493/? W/NvCpuClient﹕ Failed to bind to service
Is there something inherently wrong with the way my C code is written and called from Java that could explain this error?
How do I debug errors occurring in the C code from Android Studio?
Note: Replacing the C function body with just:
return (*env)->NewIntArray(env, 1000);
allows the call to be made to C and to return, so the binding itself works, suggesting that the error "Failed to bind to service" is simply there because the process died and cannot be invoked again from Java a second time around (I am calling this processFrame in a loop).
UPDATE: Occasionally but not always, I do also get
Consumer closed input channel or an error occurred. events=0x9
as part of the error message. I really do not see why but that does seem related to my problem somehow. I shouldn't have to release outArray from the C code. Should I?
UPDATE 2: I isolated the issue: the error is caused by a memory allocation. The code executes properly for outLength=320*480 (or anything smaller than that), but when I got to SD (640*480), I get the error I described. Is any of my memory allocation incorrect in the C code I provided? Is there a more appropriate way of allocating a larger chunk of memory from the C code? Should malloc be used instead for out? Do I need to increase heap memory and if so, how?
The error is caused by
jint out[outLength];
This does not work for too large an array. Using malloc solved my problem. If there are other issues in my code, please let me know. Otherwise I will accept the answer and move on.
I'm testing my video player on an OUYA device and it crashes almost immediately with the "dimension too large" error (see logcat dump below). If anyone knows of a work-around or a way to set a maximum dimension, I'll be grateful.
D/MySurface(2651): surfaceCreated
W/InputDispatcher(319): Attempted to unregister already unregistered input channel '41daf688 tv.ouya.console.wallpaper.OozeService (server)'
I/WindowState(319): WIN DEATH: Window{41daf688 tv.ouya.console.wallpaper.OozeService paused=false}
I/WindowManager(319): WINDOW DIED Window{41daf688 tv.ouya.console.wallpaper.OozeService paused=false}
E/SurfaceFlinger(109): dimensions too large 2560 x 1472
E/SurfaceFlinger(109): createNormalSurfaceLocked() failed (Invalid argument)
W/WindowStateAnimator(319): OutOfResourcesException creating surface
I/WindowManager(319): Out of memory for surface! Looking for leaks...
W/WindowManager(319): No leaked surfaces; killing applicatons!
W/ActivityManager(319): Killing processes Free memory at adjustment 0
W/ActivityManager(319): Killing ProcessRecord{41d9cae8 2651:example.android.player/u0a37} (adj 0): Free memory
W/WindowManager(319): Looks like we have reclaimed some memory, clearing surface for retry.
W/WindowManager(319): Due to memory failure, waiting a bit for next layout
I've tried changing the resolutions via this post as well without any success: http://forums.ouya.tv/discussion/2170/setting-resolution-not-working
I found a work around for this using a Surface callback with a pre-set max for width and height:
#Override
public void surfaceCreated(SurfaceHolder holder) {
Rect rect = holder.getSurfaceFrame();
if (rect.width() > maxWidth || rect.height() > maxHeight) {
holder.setFixedSize(maxWidth, maxHeight);
}
}
My application is closing without any error messages and re-launching automatically after switching activities approximately 20 or 30 times. I'm testing this by tapping a button that relaunches the current activity with different data (and cycles back around to the beginning once the end of the data is reached). The activity is launched with the FLAG_ACTIVITY_CLEAR_TOP flag so there should only ever be one instance of the activity in memory at a time. If I set FLAG_ACTIVITY_NO_HISTORY the problem still occurs.
This appears in the logcat output when the app crashes:
06-28 19:32:10.472: I/ActivityManager(115): Process com.mypackage.myapp (pid 3718) has died.
06-28 19:32:10.476: I/WindowManager(115): WIN DEATH: Window{406d06e0 com.mypackage.myapp/com.mypackage.myapp.welcome.LoginActivity paused=false}
06-28 19:32:10.480: E/InputDispatcher(115): channel '406ade20 com.mypackage.myapp/com.mypackage.myapp.matchup.MatchUpActivity (server)' ~ Consumer closed input channel or an error occurred. events=0x8
06-28 19:32:10.480: E/InputDispatcher(115): channel '406ade20 com.mypackage.myapp/com.mypackage.myapp.matchup.MatchUpActivity (server)' ~ Channel is unrecoverably broken and will be disposed!
06-28 19:32:10.535: I/WindowManager(115): WIN DEATH: Window{406ade20 com.mypackage.myapp/com.mypackage.myapp.matchup.MatchUpActivity paused=false}
06-28 19:32:10.539: I/WindowManager(115): WIN DEATH: Window{407a8230 com.mypackage.myapp/com.mypackage.myapp.fightcard.FightCardActivity paused=false}
06-28 19:32:10.566: I/ActivityManager(115): Start proc com.mypackage.myapp for activity com.mypackage.myapp/.fightcard.FightCardActivity: pid=3915 uid=10053 gids={3003}
06-28 19:32:10.574: I/ActivityManager(115): Low Memory: No more background processes.
Normally this would tell me that the app is leaking memory, however if I perform this test while watching the DDMS perspective in Eclipse, I can see that the allocated memory is stable, and in fact the app will often crash even when the % used is sitting around 50% (which is where it sits most of the time). The heap size and allocated memory are not increasing.
I have extensively used MAT on the app and while I did find memory leaks previously, I have solved them and can no longer find any other problems with that tool.
I've been able to recreate this problem on a Nexus S running Gingerbread, but it doesn't appear to be reproducible on a Galaxy Nexus running 4.0.4 (however that might be due to the fact that the heap is larger on the Galaxy).
What am I missing?
David Wasser's suggestion above led me to the solution. I noticed when I ran the command mentioned, I was getting a whole bunch of custom font asset allocations:
zip:/data/app/com.mypackage.myapp-2.apk:/assets/DINNextLTPro-MediumCond.otf: 98K
These allocations appeared to be increasing very quickly as I changed activities. Some googling led me to this link: http://code.google.com/p/android/issues/detail?id=9904
So it appears that there is an Android bug related to using custom fonts. The following workaround was taken from the link above:
public class Typefaces {
private static final String TAG = "Typefaces";
private static final Hashtable<String, Typeface> cache = new Hashtable<String, Typeface>();
public static Typeface get(Context c, String assetPath) {
synchronized (cache) {
if (!cache.containsKey(assetPath)) {
try {
Typeface t = Typeface.createFromAsset(c.getAssets(),
assetPath);
cache.put(assetPath, t);
} catch (Exception e) {
Log.e(TAG, "Could not get typeface '" + assetPath
+ "' because " + e.getMessage());
return null;
}
}
return cache.get(assetPath);
}
}
}
Essentially we're caching the instances of the custom font asset so each one only needs to be instantiated a single time.
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.