Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

OutOfMemoryException in the Emulator

I really dont know how to deal with this situation. All of a sudden I am getting an OutOfMemoryException on a specific Activity and it is the same everytime following the same path in the Game I am developing. I use a lot of Drawables in my Game. I read somewhere that you should perform Bitmap.recycle() often to get the memory freed. But I almost never use Bitmaps directly and the Drawabels I use, I need them to be on the screen.

It probably does not help that I am using the Facebook and Parse SDK.

Right now I am developing on my phone and in the emulator. In the emulator the heap size can not be more than 30 MB. Should I increase this number or should I leave alone and try to keep the memory consumption of my app down?

What I assumed, until recently, was that once I finish an Activity and go into the other, that everything will be garbage collected and I start fresh Example code:

private void startGamesActivity() {    
    Intent intent = new Intent(this, SS3GamesActivity.class);
    startActivity(intent);      
finish();   
}

Is this not true? Let me elaborate a little bit further. I have an Activity with two ListViews of possible Facebook Players. The OutOfMemoryException happens when I click on one of them to start a new activity which should be less memory consuming than the one before! Thats why I don't understand why the OutOfMemoryException even happens! There should be plenty of memory left after the two ListViews get garbage collected!

So what I now did was to analyze my Heap Consumption. I also downloaded the Eclipse Memory Analyzer. The biggest memory chunk that is being used is 1-byte array(byte[], boolean[]) which has a count of 702, a total size of 20,096 MB where the Largest ist 6,240 MB and 29,314 MB average. This strikes me as beeing a lot! It doesn't even encrease a lot throughout the app. But seeing as in the emulator the Heap size does not increase over 30MB it strikes as there being a lot of potential.

Using Eclipse Memory Analyzer it also gives me two possible culprits for memory leakage.

Problem Suspect 1 One instance of "android.graphics.Bitmap" loaded by "" occupies 6.543.416 (28,00%) bytes. The memory is accumulated in one instance of "byte[]" loaded by "".

Problem Suspect 2 The class "android.content.res.Resources", loaded by "", occupies 5.212.968 (22,31%) bytes. The memory is accumulated in one instance of "java.lang.Object[]" loaded by "".

The result is not so straightforwarded as I thought. I think a possible suspect judging from the class names is my LruCache. I use this to populate with Facebook Thumbnails of possible player photos as to not load them from the web everytime the user scrolls in the ListView. I am not sure why it is already so big at the beginning of my app. I already tried to make it smaller but had no luck.

This is in onCreate of my MainActivity:

// caching system
final int maxMemory = (int) (Runtime.getRuntime().maxMemory() / 1024);      
final int cacheSize = maxMemory / 16; // used to be 8, didnt help though

   mMemoryCache = new LruCache<String, Bitmap>(cacheSize) {
        @Override
        protected int sizeOf(String key, Bitmap bitmap) {
            // The cache size will be measured in kilobytes rather than
            // number of items.
            return bitmap.getByteCount() / 1024;
        }
    };  

I also checked the second possible suspect, the ressources and there is not really much I could do. There seem to be all my layout objects that I am using and I can't use less of what I am doing right now.

Also while we are at it, would something like "onDestroy" help?

public class ExampleActivity extends Activity {

    HashMap<String, String> meMap=new HashMap<String, String>();
    static ArrayList<String> meArray = new ArrayList<String>();

    protected void onDestroy() {        
        super.onDestroy();
        meMap.clear();
        meMap = null;

        meArray.clear();
        meArray = null;
    }

}

Because I didn't see much difference memory consumption thats why I assume that the gc does that aotomatically.

I would gladly take every possible suggestion to my problem. I can even provide more info but at this point I don't even know what.

StackTrace in green:

05-06 07:37:12.373: I/dalvikvm(529): Wrote stack traces to '/data/anr/traces.txt'
05-06 07:37:12.523: E/dalvikvm-heap(529): Out of memory on a 3601936-byte allocation.
05-06 07:37:12.523: I/dalvikvm(529): "main" prio=5 tid=1 RUNNABLE
05-06 07:37:12.533: I/dalvikvm(529):   | group="main" sCount=0 dsCount=0 obj=0x409c1460 self=0x12810
05-06 07:37:12.533: I/dalvikvm(529):   | sysTid=529 nice=0 sched=0/0 cgrp=default handle=1074082952
05-06 07:37:12.533: I/dalvikvm(529):   | schedstat=( 8842421920 6327029011 3892 ) utm=778 stm=106 core=0
05-06 07:37:12.533: I/dalvikvm(529):   at android.graphics.Bitmap.nativeCreate(Native Method)
05-06 07:37:12.533: I/dalvikvm(529):   at android.graphics.Bitmap.createBitmap(Bitmap.java:605)
05-06 07:37:12.533: I/dalvikvm(529):   at android.graphics.Bitmap.createBitmap(Bitmap.java:551)
05-06 07:37:12.533: I/dalvikvm(529):   at android.graphics.Bitmap.createScaledBitmap(Bitmap.java:437)
05-06 07:37:12.533: I/dalvikvm(529):   at android.graphics.BitmapFactory.finishDecode(BitmapFactory.java:524)
05-06 07:37:12.533: I/dalvikvm(529):   at android.graphics.BitmapFactory.decodeStream(BitmapFactory.java:499)
05-06 07:37:12.533: I/dalvikvm(529):   at android.graphics.BitmapFactory.decodeResourceStream(BitmapFactory.java:351)
05-06 07:37:12.533: I/dalvikvm(529):   at android.graphics.drawable.Drawable.createFromResourceStream(Drawable.java:773)
05-06 07:37:12.533: I/dalvikvm(529):   at android.content.res.Resources.loadDrawable(Resources.java:1935)
05-06 07:37:12.533: I/dalvikvm(529):   at android.content.res.TypedArray.getDrawable(TypedArray.java:601)
05-06 07:37:12.533: I/dalvikvm(529):   at android.widget.ImageView.<init>(ImageView.java:119)
05-06 07:37:12.533: I/dalvikvm(529):   at android.widget.ImageView.<init>(ImageView.java:109)
05-06 07:37:12.533: I/dalvikvm(529):   at java.lang.reflect.Constructor.constructNative(Native Method)
05-06 07:37:12.533: I/dalvikvm(529):   at java.lang.reflect.Constructor.newInstance(Constructor.java:417)
05-06 07:37:12.533: I/dalvikvm(529):   at android.view.LayoutInflater.createView(LayoutInflater.java:586)
05-06 07:37:12.533: I/dalvikvm(529):   at com.android.internal.policy.impl.PhoneLayoutInflater.onCreateView(PhoneLayoutInflater.java:56)
05-06 07:37:12.533: I/dalvikvm(529):   at android.view.LayoutInflater.onCreateView(LayoutInflater.java:653)
05-06 07:37:12.543: I/dalvikvm(529):   at android.view.LayoutInflater.createViewFromTag(LayoutInflater.java:678)
05-06 07:37:12.543: I/dalvikvm(529):   at android.view.LayoutInflater.rInflate(LayoutInflater.java:739)
05-06 07:37:12.543: I/dalvikvm(529):   at android.view.LayoutInflater.rInflate(LayoutInflater.java:742)
05-06 07:37:12.543: I/dalvikvm(529):   at android.view.LayoutInflater.inflate(LayoutInflater.java:489)
05-06 07:37:12.543: I/dalvikvm(529):   at android.view.LayoutInflater.inflate(LayoutInflater.java:396)
05-06 07:37:12.543: I/dalvikvm(529):   at android.view.LayoutInflater.inflate(LayoutInflater.java:352)
05-06 07:37:12.543: I/dalvikvm(529):   at com.android.internal.policy.impl.PhoneWindow.setContentView(PhoneWindow.java:251)
05-06 07:37:12.543: I/dalvikvm(529):   at android.app.Activity.setContentView(Activity.java:1835)
05-06 07:37:12.543: I/dalvikvm(529):   at com.quizdom.SS7ChooseTopicsActivity.onCreate(SS7ChooseTopicsActivity.java:36)
05-06 07:37:12.543: I/dalvikvm(529):   at android.app.Activity.performCreate(Activity.java:4465)
05-06 07:37:12.543: I/dalvikvm(529):   at android.app.Instrumentation.callActivityOnCreate(Instrumentation.java:1049)
05-06 07:37:12.543: I/dalvikvm(529):   at android.app.ActivityThread.performLaunchActivity(ActivityThread.java:1920)
05-06 07:37:12.543: I/dalvikvm(529):   at android.app.ActivityThread.handleLaunchActivity(ActivityThread.java:1981)
05-06 07:37:12.543: I/dalvikvm(529):   at android.app.ActivityThread.access$600(ActivityThread.java:123)
05-06 07:37:12.543: I/dalvikvm(529):   at android.app.ActivityThread$H.handleMessage(ActivityThread.java:1147)
05-06 07:37:12.543: I/dalvikvm(529):   at android.os.Handler.dispatchMessage(Handler.java:99)
05-06 07:37:12.543: I/dalvikvm(529):   at android.os.Looper.loop(Looper.java:137)
05-06 07:37:12.543: I/dalvikvm(529):   at android.app.ActivityThread.main(ActivityThread.java:4424)
05-06 07:37:12.543: I/dalvikvm(529):   at java.lang.reflect.Method.invokeNative(Native Method)
05-06 07:37:12.543: I/dalvikvm(529):   at java.lang.reflect.Method.invoke(Method.java:511)
05-06 07:37:12.543: I/dalvikvm(529):   at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:784)
05-06 07:37:12.543: I/dalvikvm(529):   at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:551)
05-06 07:37:12.543: I/dalvikvm(529):   at dalvik.system.NativeStart.main(Native Method)
like image 756
Petros Mastrantonas Avatar asked May 02 '14 15:05

Petros Mastrantonas


3 Answers

Obviously I haven't seen the bitmaps you're loading, but my guess would be that they're a lot larger than they need to be for the space you're displaying them on. If you download, for example, a 3000 x 2000 image and then assign it as the source of an ImageView that's only 300 x 200, the original Bitmap size is still stored in memory and just displayed smaller. It's important that, when downloading an image from the web, that you resize the received Bitmap to the display size before you set it as the source of the ImageView. Not doing so will consume huge chunks of memory.

This is quite a lot of code as you need to guard against lots of various error conditions during this process. The easiest solution I can offer you is to try the Picasso library. You can pass it the URL you want to download with code similar to that shown below and it will download it on a background thread, resize it and put it into the ImageView when it's ready. It will also cache the resized version so that, if you need to download the same image, it uses the cached version if it is suitable for the ImageView you're loading it for. This means you don't waste network traffic as well. If you need to do some transformations to the image before it is placed in the ImageView, you can do that as well by providing a Transform object. You can also set a placeholder image, which you should have in your drawable folders and it will display that until the actual image has been downloaded.

Picasso.with(context)
       .load("http://i.imgur.com/DvpvklR.png")
       .into(imageView);
like image 171
SDJMcHattie Avatar answered Oct 10 '22 01:10

SDJMcHattie


Basic steps

According to this answer, there are some basic steps you should follow in this case:

  • On Android 3.0+, use inBitmap on the BitmapOptions that you pass to BitmapFactory, to reuse existing memory as opposed to allocating new memory
  • recycle() your Bitmap objects when you are done with them
  • Be generally careful about your memory allocations, as Android's garbage collector is non-compacting, so eventually you will be incapable of allocating large blocks of memory again
  • Use MAT to see if you are leaking memory somewhere that is contributing to your problem.

Some posts that might be helpful

See this post about Managing Bitmap Memory, and also this about Loading Large Bitmaps. These might help you.

Also you should handle your view recycling on your ListView. See this post about Handling ListView Recycling.

Hope this helps you.

like image 2
rogcg Avatar answered Oct 10 '22 01:10

rogcg


I think the problem is that you're leaking the current Activity. You're passing this as the first parameter, which in this case is a Context. So, you're using the current activity as the context and the next thing you do is finish it. The activity gets closed, but its memory is leaked because you passed a reference to the intent.

Try this:

Intent intent = new Intent(getApplicationContext(), SS3GamesActivity.class);
like image 2
Daniel Hernández Alcojor Avatar answered Oct 09 '22 23:10

Daniel Hernández Alcojor