4

I am developing an android game application where I have about 15 screens. When I continuously play, I get an out of memory error problem randomly, sometimes in the 15th screen, and sometimes in the 12th screen or so.

Also, check out the xml below, of one the screens.

      <?xml version="1.0" encoding="UTF-8"?>
       <FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
          xmlns:tools="http://schemas.android.com/tools"
           android:id="@+id/MainFrame"
           android:layout_width="fill_parent"
           android:layout_height="fill_parent" >

   <RelativeLayout
       android:id="@+id/mainlayout"
       android:layout_width="fill_parent"
       android:layout_height="fill_parent"
       android:background="@drawable/woodenbg1" >

       <Chronometer
        android:id="@+id/chronometer"
        android:layout_width="0dp"
        android:layout_height="0dp"
        android:textSize="20sp"
        android:textStyle="bold"
        android:typeface="sans"
        android:visibility="gone" />

    <ImageView
        android:id="@+id/imageline"
        android:layout_width="10000dp"
        android:layout_height="wrap_content"
        android:layout_marginTop="105dp"
        android:src="@drawable/lineblackfornormal" />

    <ImageView
        android:id="@+id/imageviewunderalphac"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_centerHorizontal="true"
        android:layout_marginTop="350dp"
        android:src="@drawable/whitec" />

    <ImageView
        android:id="@+id/backgroundofalphab"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_centerHorizontal="true"
        android:layout_marginTop="35dp"
        android:src="@drawable/backgroundfordragimages"
        android:visibility="invisible" />

      <ImageView
          android:id="@+id/backgroundofalphaa"
          android:layout_width="wrap_content"
          android:layout_height="wrap_content"
          android:layout_marginLeft="20dp"
          android:layout_marginTop="35dp"
          android:src="@drawable/backgroundfordragimages"
          android:visibility="invisible" />

       <ImageView
           android:id="@+id/backgroundofalphac"
           android:layout_width="100dp"
           android:layout_height="wrap_content"
           android:layout_alignParentRight="true"
           android:layout_marginTop="33dp"
           android:src="@drawable/backgroundfordragimages"
           android:visibility="invisible" />

       <ImageView
          android:id="@+id/imageviewunderleftalphab"
          android:layout_width="wrap_content"
          android:layout_height="wrap_content"
          android:layout_alignParentBottom="true"
          android:layout_marginBottom="250dp"
          android:layout_marginLeft="15dp"
          android:src="@drawable/whiteb" />

      <ImageView
          android:id="@+id/imageviewunderrightalphaa"
          android:layout_width="wrap_content"
          android:layout_height="wrap_content"
          android:layout_alignParentBottom="true"
          android:layout_alignParentRight="true"
          android:layout_marginBottom="250dp"
          android:layout_marginRight="10dp"
          android:src="@drawable/whitea" />

      <ImageView
          android:id="@+id/imageviewabovealphab"
          android:layout_width="wrap_content"
          android:layout_height="wrap_content"
          android:layout_centerHorizontal="true"
          android:layout_marginTop="35dp"
          android:src="@drawable/colouredb" />

       <ImageView
          android:id="@+id/imageviewabovealphaa"
          android:layout_width="wrap_content"
          android:layout_height="wrap_content"
          android:layout_marginLeft="20dp"
          android:layout_marginTop="35dp"
          android:src="@drawable/coloureda" />

       <ImageView
          android:id="@+id/imageviewabovealphac"
          android:layout_width="wrap_content"
          android:layout_height="wrap_content"
          android:layout_alignLeft="@+id/backgroundofalphac"
          android:layout_marginTop="33dp"
          android:src="@drawable/colouredc" />
       </RelativeLayout>

     </FrameLayout>

Logcat

  05-16 12:22:18.989: E/GraphicsJNI(6745): VM won't let us allocate 2225664 bytes
  05-16 12:22:18.999: E/AndroidRuntime(6745): FATAL EXCEPTION: main
  05-16 12:22:18.999: E/AndroidRuntime(6745): java.lang.OutOfMemoryError: bitmap size exceeds VM budget
  05-16 12:22:18.999: E/AndroidRuntime(6745):     at android.graphics.Bitmap.nativeCreate(Native Method)
  05-16 12:22:18.999: E/AndroidRuntime(6745):     at android.graphics.Bitmap.createBitmap(Bitmap.java:477)
  05-16 12:22:18.999: E/AndroidRuntime(6745):     at android.graphics.Bitmap.createBitmap(Bitmap.java:444)
  05-16 12:22:18.999: E/AndroidRuntime(6745):     at android.graphics.Bitmap.createScaledBitmap(Bitmap.java:349)
  05-16 12:22:18.999: E/AndroidRuntime(6745):     at android.graphics.BitmapFactory.finishDecode(BitmapFactory.java:498)
  05-16 12:22:18.999: E/AndroidRuntime(6745):     at android.graphics.BitmapFactory.decodeStream(BitmapFactory.java:473)
  05-16 12:22:18.999: E/AndroidRuntime(6745):     at android.graphics.BitmapFactory.decodeResourceStream(BitmapFactory.java:336)
  05-16 12:22:18.999: E/AndroidRuntime(6745):     at android.graphics.drawable.Drawable.createFromResourceStream(Drawable.java:697)
  05-16 12:22:18.999: E/AndroidRuntime(6745):     at android.content.res.Resources.loadDrawable(Resources.java:1709)
  05-16 12:22:18.999: E/AndroidRuntime(6745):     at android.content.res.Resources.getDrawable(Resources.java:581)
  05-16 12:22:18.999: E/AndroidRuntime(6745):     at android.view.View.setBackgroundResource(View.java:7533)
  05-16 12:22:18.999: E/AndroidRuntime(6745):     at com.ssn.myapp.NumbersLevel3.onCreate(NumbersLevel3.java:173)
  05-16 12:22:18.999: E/AndroidRuntime(6745):     at android.app.Instrumentation.callActivityOnCreate(Instrumentation.java:1047)
  05-16 12:22:18.999: E/AndroidRuntime(6745):     at android.app.ActivityThread.performLaunchActivity(ActivityThread.java:1611)
  05-16 12:22:18.999: E/AndroidRuntime(6745):     at android.app.ActivityThread.handleLaunchActivity(ActivityThread.java:1663)
  05-16 12:22:18.999: E/AndroidRuntime(6745):     at android.app.ActivityThread.handleRelaunchActivity(ActivityThread.java:2832)
  05-16 12:22:18.999: E/AndroidRuntime(6745):     at android.app.ActivityThread.access$1600(ActivityThread.java:117)
  05-16 12:22:18.999: E/AndroidRuntime(6745):     at android.app.ActivityThread$H.handleMessage(ActivityThread.java:935)
  05-16 12:22:18.999: E/AndroidRuntime(6745):     at android.os.Handler.dispatchMessage(Handler.java:99)
  05-16 12:22:18.999: E/AndroidRuntime(6745):     at android.os.Looper.loop(Looper.java:130)
  05-16 12:22:18.999: E/AndroidRuntime(6745):     at android.app.ActivityThread.main(ActivityThread.java:3683)
  05-16 12:22:18.999: E/AndroidRuntime(6745):     at java.lang.reflect.Method.invokeNative(Native Method)
  05-16 12:22:18.999: E/AndroidRuntime(6745):     at java.lang.reflect.Method.invoke(Method.java:507)
  05-16 12:22:18.999: E/AndroidRuntime(6745):     at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:839)
  05-16 12:22:18.999: E/AndroidRuntime(6745):     at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:597)
  05-16 12:22:18.999: E/AndroidRuntime(6745):     at dalvik.system.NativeStart.main(Native Method)
mkj
  • 2,761
  • 5
  • 24
  • 28
DevAndro
  • 205
  • 1
  • 6
  • 18

2 Answers2

2

Recycle bitmaps when not in use. After honeycomb bitmaps stay on heap.

 bitmaps.recycle();

http://android-developers.blogspot.de/2009/01/avoiding-memory-leaks.html.

The link above has a topic on how to avoid memory leaks.

Also if you have high resolution image , you should scale down. See the topic under Load a Scaled Down Version into Memory.

http://developer.android.com/training/displaying-bitmaps/load-bitmap.html

Suppose you navigate form actiivty A to B. In onResume() load bitmaps. In onPause() recycle bitmaps. Do this for every activity where you load bitmaps.

You can use MAT Analyzer as shwon inthe video below to analyze and fix memeory leaks http://www.youtube.com/watch?v=_CruQY55HOk

You could use android:largeHeap in manifest under application tag.

Warning : As the heap size increases the GC kicks in more frequently and more frequently app pauses. So just because you require a bigger heap you should not use this. You can see the video above and the guy warns about the same.

 android:largeHeap

Whether your application's processes should be created with a large Dalvik heap. This applies to all processes created for the application. It only applies to the first application loaded into a process; if you're using a shared user ID to allow multiple applications to use a process, they all must use this option consistently or they will have unpredictable results.

Most apps should not need this and should instead focus on reducing their overall memory usage for improved performance. Enabling this also does not guarantee a fixed increase in available memory, because some devices are constrained by their total available memory.

To query the available memory size at runtime, use the methods getMemoryClass() or getLargeMemoryClass().

Raghunandan
  • 132,755
  • 26
  • 225
  • 256
0

It's because your images aren't getting deallocated when your activity is destroyed.

Refer this answer. Also this link too.

I don't think you should worry to the granular details when GC is executed as we don't have control over when gc gets called. Even calling gc() will not guarantee a collection. Per documentation from System.gc()

Memmory is exceeded. GC automatically collects all unused bitmaps. So voluntarily we have to remove it

Indicates to the virtual machine that it would be a good time to run the garbage collector. Note that this is a hint only. There is no guarantee that the garbage collector will actually be run.

In developing the application with large object allocation I would worry about the following instead:

  1. Upon allocating the large object and subsequently after exit the scope of life cycle of that object, do I see it get reclaimed by the GC in later Activities? This could be easily checked by running dumpsys meminfo with adb shell. You basically check after deallocation if the memory is properly garbage collected typically signify by large spike and drop afterwards
  2. Check if this large object has a clear path to GC. You could do this by checking the reference path of this object via dumping the hprof and check it in Memory Analyzer. If it does, you are safe as the GC will smart enough to collect.
  3. After I allocate this large object, do I have enough padding in my heap to execute subsequent activity? If the object is too large, there is a chance that GC is not fast enough to collect it (this is actually related to your point) and the memory consumption from subsequent activity combined with the left over from the previous ones might actually cause out of memory error. Setting null with a clear path to GC will help to ensure that this object will get GC'ed appropriately. I admit this is a problem, but my view is if this becomes a problem, we might have to relook on how this particular section is designed and see if we could do any optimization on it.
  4. Implement clean up on the activity via onDestroy as necessary and make sure the Activity is not referenced by others so it could get properly garbage collected. For example: we often pass activity context around forgetting that it will hold its reference. Things like calling recycle() on bitmap should also help. Remembering these points should help preparing more space in the case of #3 happens

I hope This may help you.

Community
  • 1
  • 1
Gunaseelan
  • 14,415
  • 11
  • 80
  • 128
  • no that's not the issue. the app exceeds memory allocated in heap. hence OOM. "It's because your images aren't getting deallocated when your activity is destroyed." this is not the issue – Raghunandan May 18 '13 at 05:06
  • `When i continuously play, i get an out of memory error problem randomly` OP only saids like this. That is why I think `images aren't getting deallocated`. – Gunaseelan May 18 '13 at 05:09
  • 1
    can u explain this part more clearly "images aren't getting deallocated". From the logcat its OOM. – Raghunandan May 18 '13 at 05:10
  • @Ginaseelan according to logcat its OOM because the bitmap size exceeds VM budget meaning the there is not enough space to be allocated for bitmaps. You should recycle bitmaps when not in use. When gc kicks in it will free memory. Secondly he should load bitmaps (images) efficiently. He should scale down the image if its too large. He should load bitmaps in onResume() and recycle it in onPause(). This makes sure when gc kicks in and activity is paused memory is freed. – Raghunandan May 18 '13 at 05:25
  • @Raghunandan Me also said the same thing. What I said `Memmory is exceeded. GC automatically collects all unused bitmaps. So voluntarily we have to remove it`. Is not Raghunandan? – Gunaseelan May 18 '13 at 05:34
  • Voluntarily you don't remove it. Its the job of the GC to free memory and there is no need for System.gc() – Raghunandan May 18 '13 at 05:35
  • See link i provided [System.gc()](http://developer.android.com/reference/java/lang/System.html#gc%28%29). Thay said `There is no guarantee that the garbage collector will actually be run.` – Gunaseelan May 18 '13 at 05:37
  • yup the garbage collector is run by the vm so whats the point of System.gc() according to the question asked here? – Raghunandan May 18 '13 at 05:38
  • @Raghunandan OP said he runs more than 15 activities. In each activity he play the game. So memory will get increased if System.gc() didn't get called. There is no guarantee System.gc() will get called. So we have to clear or memmory. Thats what I say. – Gunaseelan May 18 '13 at 05:42
  • you are wrong again. you do not clear memory. its the job of GC. you should not call Systen.gc(). The VM runs the gc if it needs to free memory. If he has 15 activities he should load image in onResume() and unbind or recycle in onPause() – Raghunandan May 18 '13 at 06:03
  • @Raghunandan Okay Ragunandan – Gunaseelan May 18 '13 at 06:05