This topic is 2179 days old which is more than the 365 day threshold we allow for new replies. Please post a new topic.

Recommended Posts

I am designing an Android game, and as the games go, they contain lots of bitmaps.

Mine is not exception and I am loading them via BitmapFactory by the following instruction:

bitmap = BitmapFactory.decodeStream(context.getResources().openRawResource(imageFileIDs[IndexOfBitmapHere])); Everything goes great, until certain amount of calls to the above instruction - then the app keels over with the Out Of Memory message. Thing is, that after uploading let say 40 bitmaps of the same size, I have still 70% of Memory Free. Then I upload just one more additional bitmap of the same size, which should take me comfortably to some 69% and instead all goes bang - OOM !!! The garbage collector suddenly reports it has only 3-2-1% of memory left, then OOM crash. There is obviously some bug in Android (I am running this on 3.0) and I cannot figure out what it is. I have tried just about everything and now I am stuck with this annoying issue already 2 weeks. Does anybody know what causes it and what is the way around ? Any help is highly appreciated.

Share on other sites
Android applications are limited to 16MB/24MB of RAM. See here for finding out how much you have available.

It seems to have been increased somewhat as well.

Does anybody know what causes it and what is the way around ?[/quote]

Share on other sites

Android applications are limited to 16MB/24MB of RAM.

Obviously you weren't reading my question properly. There is 48MB memory btw in Android 3.0, and some 70% are still free before it all goes balls up!

Share on other sites

Yeah, I also found that few days ago. That does not show how to solve the problem, only shows how to prevent OOM by not loading the bitmap. What kind of lame solution is that?

Share on other sites
Are you using LogCat to track the exact nature of the exception and get the stack trace? (It's sometimes not the easiest thing to read but with the latest ADT for Eclipse it is much easier to filter).

When I was seeing our OOM exception LogCat was able to tell me the resource Id i was loading and how much memory it was trying to allocate (With the stack trace it was more than enough to diagnose the cause)? It might be helpful if you post that up (when i saw we were allocating 10 megs just to draw 1 background I knew exactly what was up and resolving it was not an issue).

You are more than likely already aware that the .png's are small in their compressed state but once loaded into memory they are uncompressed bitmaps so will consume (width x height x bytesperpixel) bytes. It is also extremely easy to leak memory in Android but I don't believe this is your issue from your original post.

We had a few issues with the memory management on Android initially but once we refactored our engine to be a bit more Android friendly we were fine.

Share on other sites
Thanks Fiallen, for trying to help me with this. You seem to know what are you talking about (as opposed to Antheus above, who just googles and stabs in the dark)

This is my first game in Android and I already thought I have a fully working game engine, which I was designing by myself from scratch, and can concentrate on the level design, when this setback with textures, which sent me right back to the square one

Are you using LogCat to track the exact nature of the exception and get the stack trace?

Yeah, sure I use that from beginning, it's extremely useful, and now when you mentioned it, I've looked into it really in detail again, but still cannot figure out what I am doing wrong.

When I was seeing our OOM exception LogCat was able to tell me the resource Id i was loading and how much memory it was trying to allocate (With the stack trace it was more than enough to diagnose the cause)? You are more than likely already aware that the .png's are small in their compressed state but once loaded into memory they are uncompressed bitmaps so will consume (width x height x bytesperpixel) bytes. It is also extremely easy to leak memory in Android but I don't believe this is your issue from your original post.

This is where I am really getting confused. I was originally allocating well less than width x height x 4, and it still worked (see first log) and when I increased that significantly, it seemed to make no difference in amount of allocated memory. As you will see from log there is always this magical number 1925136 bytes floating around, even I increase allocation by thousandfold.

For clarity, I am posting 2 logs in following two posts.

1st log is where the game starts fine, about 50 or so textures ranging in size from 80x80 to 1280x752 load just fine and everything works great. This works whether I put to allocation ridiculously small numbers, or their thousedfold values (width * height * 4 does not work, that crashes immediately, but I put there anything from 12 * 4 to width * 4 and it did not seem to make an iota of difference - all textures loaded and game ran). As you can see though there is something strange going on there - it seems like Android is running out of puff there, having 1-2-3% of memory free, then suddenly reclaims up to 71%!

2nd log is the same situation, I did not change memory allocation, only added one more object loading one more 1280x752 texture. That is where it crashes.

I figured that I just can null some texture objects/classes and reinstate them later when I need them, but I cannot figure out how to do it either. See:

http://www.gamedev.n...__fromsearch__1

Either Android/Java is extremely difficult environment, or I am a lousy programmer

I am stuck with this problem now about 2 weeks and will be very grateful if you could show me the way out of this mess.

Share on other sites
LOG - NO CRASH

02-01 20:54:58.323: D/dalvikvm(542): GC_FOR_ALLOC freed 48K, 5% free 6254K/6531K, paused 295ms
02-01 20:54:58.391: I/dalvikvm-heap(542): Grow heap (frag case) to 8.034MB for 1925136-byte allocation
02-01 20:54:58.561: D/dalvikvm(542): GC_FOR_ALLOC freed <1K, 4% free 8134K/8455K, paused 60ms
02-01 20:54:58.661: D/dalvikvm(542): GC_CONCURRENT freed <1K, 4% free 8134K/8455K, paused 3ms+2ms
02-01 20:54:59.211: D/dalvikvm(542): GC_EXPLICIT freed 9K, 4% free 8124K/8455K, paused 3ms+5ms
02-01 20:54:59.271: D/dalvikvm(542): GC_FOR_ALLOC freed <1K, 4% free 8145K/8455K, paused 50ms
02-01 20:54:59.291: I/dalvikvm-heap(542): Grow heap (frag case) to 9.889MB for 1925136-byte allocation
02-01 20:54:59.451: D/dalvikvm(542): GC_FOR_ALLOC freed 0K, 4% free 10025K/10375K, paused 69ms
02-01 20:54:59.551: D/dalvikvm(542): GC_CONCURRENT freed <1K, 4% free 10025K/10375K, paused 4ms+2ms
02-01 20:55:00.253: D/dalvikvm(542): GC_EXPLICIT freed 2K, 4% free 10023K/10375K, paused 3ms+5ms
02-01 20:55:00.321: D/dalvikvm(542): GC_FOR_ALLOC freed <1K, 4% free 10043K/10375K, paused 64ms
02-01 20:55:00.344: I/dalvikvm-heap(542): Grow heap (frag case) to 11.746MB for 1925136-byte allocation
02-01 20:55:00.421: D/dalvikvm(542): GC_FOR_ALLOC freed 0K, 4% free 11923K/12295K, paused 48ms
02-01 20:55:00.551: D/dalvikvm(542): GC_CONCURRENT freed 0K, 4% free 11923K/12295K, paused 4ms+2ms
02-01 20:55:01.742: D/dalvikvm(542): GC_EXPLICIT freed <1K, 4% free 11923K/12295K, paused 2ms+3ms
02-01 20:55:01.902: D/dalvikvm(542): GC_FOR_ALLOC freed <1K, 3% free 11944K/12295K, paused 151ms
02-01 20:55:01.954: I/dalvikvm-heap(542): Grow heap (frag case) to 13.601MB for 1925136-byte allocation
02-01 20:55:02.083: D/dalvikvm(542): GC_FOR_ALLOC freed 0K, 3% free 13824K/14215K, paused 98ms
02-01 20:55:02.212: D/dalvikvm(542): GC_CONCURRENT freed <1K, 3% free 13824K/14215K, paused 3ms+2ms
02-01 20:55:02.792: D/dalvikvm(542): GC_EXPLICIT freed <1K, 3% free 13824K/14215K, paused 5ms+3ms
02-01 20:55:02.852: D/dalvikvm(542): GC_FOR_ALLOC freed <1K, 3% free 13845K/14215K, paused 59ms
02-01 20:55:02.872: I/dalvikvm-heap(542): Grow heap (frag case) to 15.458MB for 1925136-byte allocation
02-01 20:55:03.032: D/dalvikvm(542): GC_FOR_ALLOC freed 0K, 3% free 15725K/16135K, paused 69ms
02-01 20:55:03.643: D/dalvikvm(542): GC_EXPLICIT freed <1K, 3% free 15725K/16135K, paused 5ms+3ms
02-01 20:55:03.701: D/dalvikvm(542): GC_FOR_ALLOC freed <1K, 3% free 15746K/16135K, paused 59ms
02-01 20:55:03.732: I/dalvikvm-heap(542): Grow heap (frag case) to 17.314MB for 1925136-byte allocation
02-01 20:55:03.872: D/dalvikvm(542): GC_FOR_ALLOC freed 0K, 3% free 17626K/18055K, paused 57ms
02-01 20:55:04.472: D/dalvikvm(542): GC_EXPLICIT freed <1K, 3% free 17626K/18055K, paused 3ms+5ms
02-01 20:55:04.532: D/dalvikvm(542): GC_FOR_ALLOC freed <1K, 3% free 17646K/18055K, paused 57ms
02-01 20:55:04.552: I/dalvikvm-heap(542): Grow heap (frag case) to 19.170MB for 1925136-byte allocation
02-01 20:55:04.721: D/dalvikvm(542): GC_FOR_ALLOC freed 0K, 3% free 19526K/19975K, paused 77ms
02-01 20:55:05.331: D/dalvikvm(542): GC_EXPLICIT freed <1K, 3% free 19526K/19975K, paused 2ms+3ms
02-01 20:55:05.421: D/dalvikvm(542): GC_EXPLICIT freed <1K, 2% free 19586K/19975K, paused 6ms+3ms
02-01 20:55:05.532: D/dalvikvm(542): GC_EXPLICIT freed <1K, 2% free 19650K/19975K, paused 3ms+3ms
02-01 20:55:05.651: D/dalvikvm(542): GC_EXPLICIT freed <1K, 2% free 19713K/19975K, paused 3ms+3ms
02-01 20:55:05.771: D/dalvikvm(542): GC_EXPLICIT freed <1K, 1% free 19776K/19975K, paused 3ms+3ms
02-01 20:55:05.891: D/dalvikvm(542): GC_EXPLICIT freed <1K, 1% free 19839K/20039K, paused 3ms+2ms
...HERE MANY MORE LINES JUMPING BETWEEN 1-3% free
02-01 20:55:09.311: D/dalvikvm(542): GC_EXPLICIT freed <1K, 3% free 31092K/31751K, paused 6ms+4ms
02-01 20:55:09.441: D/dalvikvm(542): GC_EXPLICIT freed <1K, 3% free 31645K/32327K, paused 4ms+4ms
02-01 20:55:09.571: D/dalvikvm(542): GC_EXPLICIT freed <1K, 3% free 32198K/32903K, paused 2ms+6ms
02-01 20:55:09.691: D/dalvikvm(542): GC_EXPLICIT freed <1K, 1% free 32752K/32903K, paused 2ms+3ms
02-01 20:55:09.881: D/dalvikvm(542): GC_EXPLICIT freed <1K, 1% free 33108K/33287K, paused 2ms+3ms
02-01 20:55:10.061: D/dalvikvm(542): GC_EXPLICIT freed <1K, 1% free 33446K/33671K, paused 2ms+3ms
02-01 20:55:10.321: D/dalvikvm(542): GC_CONCURRENT freed <1K, 1% free 35427K/35655K, paused 9ms+13ms
02-01 20:55:10.481: D/dalvikvm(542): GC_EXPLICIT freed <1K, 1% free 35427K/35655K, paused 3ms+3ms
02-01 20:55:10.721: D/dalvikvm(542): GC_EXPLICIT freed <1K, 1% free 37352K/37639K, paused 3ms+6ms
02-01 20:55:10.801: D/dalvikvm(542): GC_FOR_ALLOC freed <1K, 1% free 37352K/37639K, paused 74ms
02-01 20:55:10.821: I/dalvikvm-heap(542): Grow heap (frag case) to 38.458MB for 1971216-byte allocation
02-01 20:55:11.001: D/dalvikvm(542): GC_FOR_ALLOC freed 0K, 1% free 39277K/39623K, paused 88ms
02-01 20:55:11.252: D/dalvikvm(542): GC_EXPLICIT freed <1K, 1% free 39277K/39623K, paused 3ms+3ms
02-01 20:55:11.321: D/dalvikvm(542): GC_FOR_ALLOC freed <1K, 1% free 39277K/39623K, paused 74ms
02-01 20:55:11.351: I/dalvikvm-heap(542): Grow heap (frag case) to 40.337MB for 1971216-byte allocation
02-01 20:55:11.541: D/dalvikvm(542): GC_FOR_ALLOC freed 0K, 1% free 41202K/41607K, paused 100ms
02-01 20:55:11.791: D/dalvikvm(542): GC_EXPLICIT freed <1K, 1% free 41202K/41607K, paused 3ms+4ms
...HERE MANY MORE LINES 1% free
02-01 20:55:15.521: D/dalvikvm(542): GC_EXPLICIT freed <1K, 1% free 42013K/42119K, paused 3ms+4ms
02-01 20:55:15.811: D/dalvikvm(542): GC_CONCURRENT freed <1K, 1% free 43954K/44103K, paused 9ms+3ms
02-01 20:55:16.281: D/dalvikvm(542): GC_EXPLICIT freed <1K, 1% free 43954K/44103K, paused 5ms+6ms
02-01 20:55:16.951: D/dalvikvm(542): GC_EXPLICIT freed <1K, 1% free 45834K/46023K, paused 3ms+3ms
02-01 20:55:17.501: I/dalvikvm-heap(542): Clamp target GC heap from 48.698MB to 48.000MB
02-01 20:55:17.501: D/dalvikvm(542): GC_EXPLICIT freed <1K, 1% free 47714K/47943K, paused 5ms+3ms
02-01 20:55:17.641: D/MEMORY(542): Max Memory: 49152 KB
02-01 20:55:17.891: D/LOADED:(542): Start: Level 1
02-01 20:55:19.441: D/SCREEN SIZE(542): ScreenX: 1280, ScreenY: 752
02-01 20:55:19.601: I/ARMAssembler(542): generated scanline__000001B7:030101C4_00001404_00000000 [ 65 ipp] (130 ins) at [0x49a281e8:0x49a283f0] in 3250574 ns
02-01 20:55:19.821: I/ARMAssembler(542): generated scanline__000001B7:035454C4_00001404_00000000 [ 97 ipp] (183 ins) at [0x49a283f8:0x49a286d4] in 1737577 ns
02-01 20:55:19.953: I/ARMAssembler(542): generated scanline__000001B7:035454C4_00009404_00000000 [160 ipp] (246 ins) at [0x49a286d8:0x49a28ab0] in 1681797 ns
02-01 20:55:24.431: I/ARMAssembler(542): generated scanline__000001B7:035454C4_00001401_00000000 [105 ipp] (191 ins) at [0x49a28ab8:0x49a28db4] in 1772941 ns
02-01 20:55:48.503: D/dalvikvm(542): GC_CONCURRENT freed 34563K, 71% free 14376K/49095K, paused 9ms+44ms
02-01 20:56:13.161: D/dalvikvm(542): GC_CONCURRENT freed 1932K, 71% free 14367K/49095K, paused 10ms+16ms
...HERE MANY MORE LINES 71% free
02-01 20:59:33.453: D/dalvikvm(542): GC_CONCURRENT freed 1923K, 71% free 14367K/49095K, paused 3ms+3ms
02-01 21:00:03.051: D/dalvikvm(542): GC_CONCURRENT freed 1923K, 71% free 14367K/49095K, paused 4ms+3ms

Share on other sites
LOG - CRASH

02-01 21:00:18.361: D/dalvikvm(596): GC_FOR_ALLOC freed 48K, 5% free 6254K/6531K, paused 341ms
02-01 21:00:18.453: I/dalvikvm-heap(596): Grow heap (frag case) to 8.034MB for 1925136-byte allocation
02-01 21:00:18.551: D/dalvikvm(596): GC_FOR_ALLOC freed <1K, 4% free 8134K/8455K, paused 59ms
02-01 21:00:18.661: D/dalvikvm(596): GC_CONCURRENT freed <1K, 4% free 8134K/8455K, paused 3ms+12ms
02-01 21:00:19.201: D/dalvikvm(596): GC_EXPLICIT freed 9K, 4% free 8124K/8455K, paused 2ms+2ms
02-01 21:00:19.253: D/dalvikvm(596): GC_FOR_ALLOC freed <1K, 4% free 8145K/8455K, paused 53ms
02-01 21:00:19.281: I/dalvikvm-heap(596): Grow heap (frag case) to 9.889MB for 1925136-byte allocation
02-01 21:00:19.432: D/dalvikvm(596): GC_FOR_ALLOC freed 0K, 4% free 10025K/10375K, paused 62ms
02-01 21:00:19.522: D/dalvikvm(596): GC_CONCURRENT freed <1K, 4% free 10025K/10375K, paused 3ms+2ms
02-01 21:00:20.121: D/dalvikvm(596): GC_EXPLICIT freed 2K, 4% free 10023K/10375K, paused 3ms+2ms
02-01 21:00:20.191: D/dalvikvm(596): GC_FOR_ALLOC freed <1K, 4% free 10043K/10375K, paused 63ms
02-01 21:00:20.211: I/dalvikvm-heap(596): Grow heap (frag case) to 11.746MB for 1925136-byte allocation
02-01 21:00:20.291: D/dalvikvm(596): GC_FOR_ALLOC freed 0K, 4% free 11923K/12295K, paused 48ms
02-01 21:00:20.411: D/dalvikvm(596): GC_CONCURRENT freed <1K, 4% free 11923K/12295K, paused 3ms+3ms
02-01 21:00:21.593: D/dalvikvm(596): GC_EXPLICIT freed <1K, 4% free 11923K/12295K, paused 3ms+3ms
02-01 21:00:21.683: D/dalvikvm(596): GC_FOR_ALLOC freed <1K, 3% free 11944K/12295K, paused 90ms
02-01 21:00:21.721: I/dalvikvm-heap(596): Grow heap (frag case) to 13.601MB for 1925136-byte allocation
02-01 21:00:21.871: D/dalvikvm(596): GC_FOR_ALLOC freed 0K, 3% free 13824K/14215K, paused 89ms
02-01 21:00:22.043: D/dalvikvm(596): GC_CONCURRENT freed <1K, 3% free 13824K/14215K, paused 3ms+4ms
02-01 21:00:22.711: D/dalvikvm(596): GC_EXPLICIT freed <1K, 3% free 13824K/14215K, paused 3ms+5ms
02-01 21:00:22.781: D/dalvikvm(596): GC_FOR_ALLOC freed <1K, 3% free 13845K/14215K, paused 60ms
02-01 21:00:22.801: I/dalvikvm-heap(596): Grow heap (frag case) to 15.458MB for 1925136-byte allocation
02-01 21:00:22.941: D/dalvikvm(596): GC_FOR_ALLOC freed 0K, 3% free 15725K/16135K, paused 58ms
02-01 21:00:23.551: D/dalvikvm(596): GC_EXPLICIT freed <1K, 3% free 15725K/16135K, paused 2ms+3ms
02-01 21:00:23.611: D/dalvikvm(596): GC_FOR_ALLOC freed <1K, 3% free 15746K/16135K, paused 56ms
02-01 21:00:23.631: I/dalvikvm-heap(596): Grow heap (frag case) to 17.314MB for 1925136-byte allocation
02-01 21:00:23.801: D/dalvikvm(596): GC_FOR_ALLOC freed 0K, 3% free 17626K/18055K, paused 74ms
02-01 21:00:24.391: D/dalvikvm(596): GC_EXPLICIT freed <1K, 3% free 17626K/18055K, paused 5ms+3ms
02-01 21:00:24.461: D/dalvikvm(596): GC_FOR_ALLOC freed <1K, 3% free 17646K/18055K, paused 59ms
02-01 21:00:24.481: I/dalvikvm-heap(596): Grow heap (frag case) to 19.170MB for 1925136-byte allocation
02-01 21:00:24.661: D/dalvikvm(596): GC_FOR_ALLOC freed 0K, 3% free 19526K/19975K, paused 82ms
02-01 21:00:25.261: D/dalvikvm(596): GC_EXPLICIT freed <1K, 3% free 19526K/19975K, paused 3ms+3ms
02-01 21:00:25.321: D/dalvikvm(596): GC_FOR_ALLOC freed <1K, 3% free 19547K/19975K, paused 60ms
02-01 21:00:25.343: I/dalvikvm-heap(596): Grow heap (frag case) to 21.027MB for 1925136-byte allocation
02-01 21:00:25.511: D/dalvikvm(596): GC_FOR_ALLOC freed 0K, 3% free 21427K/21895K, paused 79ms
02-01 21:00:26.121: D/dalvikvm(596): GC_EXPLICIT freed <1K, 3% free 21427K/21895K, paused 3ms+5ms
02-01 21:00:26.221: D/dalvikvm(596): GC_EXPLICIT freed <1K, 2% free 21487K/21895K, paused 3ms+3ms
02-01 21:00:26.343: D/dalvikvm(596): GC_EXPLICIT freed <1K, 2% free 21550K/21895K, paused 3ms+5ms
02-01 21:00:26.461: D/dalvikvm(596): GC_EXPLICIT freed <1K, 2% free 21614K/21895K, paused 9ms+3ms
02-01 21:00:26.581: D/dalvikvm(596): GC_EXPLICIT freed <1K, 1% free 21677K/21895K, paused 3ms+3ms
02-01 21:00:26.701: D/dalvikvm(596): GC_EXPLICIT freed <1K, 1% free 21740K/21895K, paused 3ms+3ms
02-01 21:00:26.821: D/dalvikvm(596): GC_EXPLICIT freed <1K, 1% free 21803K/21959K, paused 3ms+3ms
02-01 21:00:26.941: D/dalvikvm(596): GC_EXPLICIT freed <1K, 1% free 21866K/22023K, paused 2ms+6ms
...HERE MANY MORE LINES JUMPING BETWEEN 1-3% free
02-01 21:00:30.232: D/dalvikvm(596): GC_EXPLICIT freed <1K, 3% free 32993K/33671K, paused 3ms+6ms
02-01 21:00:30.362: D/dalvikvm(596): GC_EXPLICIT freed <1K, 3% free 33546K/34247K, paused 2ms+4ms
02-01 21:00:30.492: D/dalvikvm(596): GC_EXPLICIT freed <1K, 1% free 34099K/34247K, paused 3ms+3ms
02-01 21:00:30.622: D/dalvikvm(596): GC_EXPLICIT freed <1K, 1% free 34653K/34823K, paused 3ms+3ms
02-01 21:00:30.814: D/dalvikvm(596): GC_EXPLICIT freed <1K, 1% free 35009K/35207K, paused 2ms+3ms
02-01 21:00:30.992: D/dalvikvm(596): GC_EXPLICIT freed <1K, 1% free 35346K/35591K, paused 2ms+3ms
02-01 21:00:31.072: D/dalvikvm(596): GC_FOR_ALLOC freed <1K, 1% free 35402K/35591K, paused 78ms
02-01 21:00:31.311: D/dalvikvm(596): GC_EXPLICIT freed <1K, 1% free 37327K/37575K, paused 3ms+4ms
02-01 21:00:31.383: D/dalvikvm(596): GC_FOR_ALLOC freed <1K, 1% free 37327K/37575K, paused 74ms
02-01 21:00:31.411: I/dalvikvm-heap(596): Grow heap (frag case) to 38.434MB for 1971216-byte allocation
02-01 21:00:31.601: D/dalvikvm(596): GC_FOR_ALLOC freed 0K, 1% free 39252K/39559K, paused 95ms
02-01 21:00:31.801: D/dalvikvm(596): GC_EXPLICIT freed <1K, 1% free 39253K/39559K, paused 5ms+3ms
02-01 21:00:31.911: D/dalvikvm(596): GC_FOR_ALLOC freed <1K, 1% free 39253K/39559K, paused 100ms
02-01 21:00:31.931: I/dalvikvm-heap(596): Grow heap (frag case) to 40.314MB for 1971216-byte allocation
02-01 21:00:32.121: D/dalvikvm(596): GC_FOR_ALLOC freed 0K, 1% free 41178K/41543K, paused 99ms
02-01 21:00:32.371: D/dalvikvm(596): GC_EXPLICIT freed <1K, 1% free 41178K/41543K, paused 2ms+4ms
02-01 21:00:32.441: D/dalvikvm(596): GC_FOR_ALLOC freed <1K, 1% free 41178K/41543K, paused 78ms
02-01 21:00:32.471: I/dalvikvm-heap(596): Grow heap (frag case) to 42.194MB for 1971216-byte allocation
02-01 21:00:32.681: D/dalvikvm(596): GC_FOR_ALLOC freed 0K, 1% free 43103K/43527K, paused 114ms
...HERE MANY MORE LINES JUMPING BETWEEN 1% free
02-01 21:00:36.701: D/dalvikvm(596): GC_EXPLICIT freed <1K, 1% free 43914K/44039K, paused 3ms+3ms
02-01 21:00:36.951: D/dalvikvm(596): GC_CONCURRENT freed <1K, 1% free 45855K/46023K, paused 4ms+3ms
02-01 21:00:37.423: D/dalvikvm(596): GC_EXPLICIT freed <1K, 1% free 45855K/46023K, paused 3ms+4ms
02-01 21:00:38.141: I/dalvikvm-heap(596): Clamp target GC heap from 48.717MB to 48.000MB
02-01 21:00:38.141: D/dalvikvm(596): GC_EXPLICIT freed <1K, 1% free 47735K/47943K, paused 3ms+6ms
02-01 21:00:38.241: I/dalvikvm-heap(596): Clamp target GC heap from 48.718MB to 48.000MB
02-01 21:00:38.241: D/dalvikvm(596): GC_FOR_ALLOC freed <1K, 1% free 47735K/47943K, paused 100ms
02-01 21:00:38.241: I/dalvikvm-heap(596): Forcing collection of SoftReferences for 1925136-byte allocation
02-01 21:00:38.351: I/dalvikvm-heap(596): Clamp target GC heap from 48.716MB to 48.000MB
02-01 21:00:38.351: D/dalvikvm(596): GC_BEFORE_OOM freed 1K, 1% free 47734K/47943K, paused 109ms
[color=#ff0000]02-01 21:00:38.351: E/dalvikvm-heap(596): Out of memory on a 1925136-byte allocation.
02-01 21:00:38.351: I/dalvikvm(596): "main" prio=5 tid=1 RUNNABLE
02-01 21:00:38.351: I/dalvikvm(596): | group="main" sCount=0 dsCount=0 obj=0x4001e6b0 self=0x115b8
02-01 21:00:38.351: I/dalvikvm(596): | sysTid=596 nice=0 sched=0/0 cgrp=default handle=-1342909328
02-01 21:00:38.361: I/dalvikvm(596): | schedstat=( 16874792266 2631344068 909 ) utm=1557 stm=130 core=0
02-01 21:00:38.361: I/dalvikvm(596): at android.graphics.BitmapFactory.nativeDecodeAsset(Native Method)
02-01 21:00:38.361: I/dalvikvm(596): at android.graphics.BitmapFactory.decodeStream(BitmapFactory.java:483)
02-01 21:00:38.361: I/dalvikvm(596): at android.graphics.BitmapFactory.decodeStream(BitmapFactory.java:549)
02-01 21:00:38.361: I/dalvikvm(596): at Atomix.apk.Background.<init>(Background.java:57)
02-01 21:00:38.361: I/dalvikvm(596): at Atomix.apk.GLRenderer.<init>(GLRenderer.java:65)
02-01 21:00:38.361: I/dalvikvm(596): at Atomix.apk.AtomixActivity.onCreate(AtomixActivity.java:49)
02-01 21:00:38.361: I/dalvikvm(596): at android.app.Instrumentation.callActivityOnCreate(Instrumentation.java:1048)
02-01 21:00:38.361: I/dalvikvm(596): at android.app.ActivityThread.access$1500(ActivityThread.java:123) 02-01 21:00:38.361: I/dalvikvm(596): at android.app.ActivityThread$H.handleMessage(ActivityThread.java:993)
02-01 21:00:38.361: I/dalvikvm(596): at android.os.Handler.dispatchMessage(Handler.java:99)
02-01 21:00:38.361: I/dalvikvm(596): at android.os.Looper.loop(Looper.java:126)
02-01 21:00:38.361: I/dalvikvm(596): at java.lang.reflect.Method.invokeNative(Native Method)
02-01 21:00:38.361: I/dalvikvm(596): at java.lang.reflect.Method.invoke(Method.java:491)
02-01 21:00:38.361: I/dalvikvm(596): at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:841) 02-01 21:00:38.361: I/dalvikvm(596): at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:599) 02-01 21:00:38.361: I/dalvikvm(596): at dalvik.system.NativeStart.main(Native Method) 02-01 21:00:38.371: D/skia(596): --- decoder->decode returned false 02-01 21:00:38.371: D/AndroidRuntime(596): Shutting down VM 02-01 21:00:38.371: W/dalvikvm(596): threadid=1: thread exiting with uncaught exception (group=0x40014760) [color=#ff0000]02-01 21:00:38.381: E/AndroidRuntime(596): FATAL EXCEPTION: main 02-01 21:00:38.381: E/AndroidRuntime(596): java.lang.OutOfMemoryError 02-01 21:00:38.381: E/AndroidRuntime(596): at android.graphics.BitmapFactory.nativeDecodeAsset(Native Method) 02-01 21:00:38.381: E/AndroidRuntime(596): at android.graphics.BitmapFactory.decodeStream(BitmapFactory.java:483) 02-01 21:00:38.381: E/AndroidRuntime(596): at android.graphics.BitmapFactory.decodeStream(BitmapFactory.java:549) 02-01 21:00:38.381: E/AndroidRuntime(596): at Atomix.apk.Background.<init>(Background.java:57) 02-01 21:00:38.381: E/AndroidRuntime(596): at Atomix.apk.GLRenderer.<init>(GLRenderer.java:65) 02-01 21:00:38.381: E/AndroidRuntime(596): at Atomix.apk.AtomixActivity.onCreate(AtomixActivity.java:49) 02-01 21:00:38.381: E/AndroidRuntime(596): at android.app.Instrumentation.callActivityOnCreate(Instrumentation.java:1048) 02-01 21:00:38.381: E/AndroidRuntime(596): at android.app.ActivityThread.performLaunchActivity(ActivityThread.java:1700) 02-01 21:00:38.381: E/AndroidRuntime(596): at android.app.ActivityThread.handleLaunchActivity(ActivityThread.java:1752) 02-01 21:00:38.381: E/AndroidRuntime(596): at android.app.ActivityThread.access$1500(ActivityThread.java:123)
02-01 21:00:38.381: E/AndroidRuntime(596): at android.app.ActivityThread$H.handleMessage(ActivityThread.java:993) 02-01 21:00:38.381: E/AndroidRuntime(596): at android.os.Handler.dispatchMessage(Handler.java:99) 02-01 21:00:38.381: E/AndroidRuntime(596): at android.os.Looper.loop(Looper.java:126) 02-01 21:00:38.381: E/AndroidRuntime(596): at android.app.ActivityThread.main(ActivityThread.java:3997) 02-01 21:00:38.381: E/AndroidRuntime(596): at java.lang.reflect.Method.invokeNative(Native Method) 02-01 21:00:38.381: E/AndroidRuntime(596): at java.lang.reflect.Method.invoke(Method.java:491) 02-01 21:00:38.381: E/AndroidRuntime(596): at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:841)
02-01 21:00:38.381: E/AndroidRuntime(596): at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:599)
02-01 21:00:38.381: E/AndroidRuntime(596): at dalvik.system.NativeStart.main(Native Method)

Share on other sites

Thanks Fiallen, for trying to help me with this. You seem to know what are you talking about (as opposed to Antheus above, who just googles and stabs in the dark)

I googled because your description of the problem was simplistic at the level of someone spent last two weeks pressing the same button expecting different result.

To get an informed answer, provide a list of things that you've tried and result, along with various other attempts at the solution, links to bug reports and everything else.

While you're at it, lose the entitled and arrogant attitude. That DOES make you a lousy programmer.

This is a free for all forum, not your personal help line.

Share on other sites
This is a free for all forum, not your personal help line.

I never doubted that / thought that, respectively.

Share on other sites
Now is midnight and I am still looking into this. Just found this interesting article from Google I/O 2011:

ERRATUM: I’d like to correct one mistake I made in the Q&A, answering a question at 49:33 in the video. I said that “garbage collection is fundamentally kind of asynchronous; if you’re really close to the limit there can be times that you’re trying to allocate so fast that the garbage collector can’t keep up.” That isn’t really true. Dalvik will always perform a full, synchronous GC before thowing an OutOfMemoryError. What I was referring to here was finalization. [color=#ff0000]Finalization is asynchronous, so if you are close to your heap limit and allocating a lot of objects, you may run out of memory before finalizable objects can be freed.

Could this be related to my problem ?

Share on other sites
It certainly could be related to your problem as you are doing quite a few allocations very quickly and are running dangerously close to the limit (I don't think we come anywhere near that in terms of memory usage).

Certainly the Garbage collector is doing the correct thing before your OOM and having one last shot at freeing stuff up. If you are leaking memory (holding onto it for longer than you intended) then this might be an issue (particularly as you are so close to the 48MB limit). You will see times where things work fine and then others where the Garbage Collector is unable to collect something you thought should be freed up and just can't collect enough to carry on (We saw something similar flicking back and forwards on our menu screens as they had large backgrounds and we leaked some memory each time). Does anything happen in your game before this loading of the images? (A menu system, other activities as leaking the context is quite common).

Was there anything particularly different in the scenes for the log where you have the successful result and the crash? or were they exactly the same test? (Was it running on the phone or the emulator?). It looks like a pre load of all the resources before starting a level (which is definately the right thing to do).

Given you are so close to the 48MB limit you might want to look into how your resources (pngs, oggs so on and so forth) are setup (I can't comment on this as I don't know much about your project but I know I had to drag our artists back into the real world of having to be as conservative as they could).

I will post an answer on your other post just now which might clear things up a bit in terms of how to structure your code (not that I am an expert by any stretch of the imagination).

Share on other sites

Was there anything particularly different in the scenes for the log where you have the successful result and the crash? or were they exactly the same test?

For clarity, I am posting 2 logs in following two posts.

1st log is where the game starts fine, about 50 or so textures ranging in size from 80x80 to 1280x752 load just fine and everything works great. This works whether I put to allocation ridiculously small numbers, or their thousedfold values (width * height * 4 does not work, that crashes immediately, but I put there anything from 12 * 4 to width * 4 and it did not seem to make an iota of difference - all textures loaded and game ran). As you can see though there is something strange going on there - it seems like Android is running out of puff there, having 1-2-3% of memory free, then suddenly reclaims up to 71%!

2nd log is the same situation, I did not change memory allocation, only added one more object loading one more 1280x752 texture. That is where it crashes.

As you can see, there is no way how 50 bitmaps take 29% of the memory and then only 1 additional bitmap takes remaining 71% - there must be something else going on, most likely too many allocations too quickly, as we both concluded above. This is why I would prefer to load only bitmaps for one level at a time which would completely solve all my problems. But how to do it, if I need for that the damned Context and I cannot get it within onDrawFrame function ???

Was it running on the phone or the emulator?

Both apps related to the above logs were running on the emulator. Then I tried to run it on the actual device, and it loads some 10 bitmaps more before it goes OOM. This probably relates to speed of GC which would be much quicker on the device than on the emulator. This still does not solve my problem though, as that is still only about 1/3 of all the bitmaps which I need to load for the whole game.

It looks like a pre load of all the resources before starting a level (which is definately the right thing to do).

In fact, I am trying to load all the bitmaps which I need for the whole game. I don't think this is the right thing to do, I would much more prefer to load only the bitmaps which I need for the first level, and load bitmaps for another level as and when I start that particular level, but I don't know how to do it either - to load the bitmaps later, I need the Context and I don't know how to pass it into onDrawFrame function, where is my main game loop (as explained in my other post http://www.gamedev.net/topic/619474-how-to-pass-context-into-ondrawframe/page__fromsearch__1

Share on other sites

In fact, I am trying to load all the bitmaps which I need for the whole game. I don't think this is the right thing to do, I would much more prefer to load only the bitmaps which I need for the first level, and load bitmaps for another level as and when I start that particular level, but I don't know how to do it either - to load the bitmaps later, I need the Context and I don't know how to pass it into onDrawFrame function, where is my main game loop (as explained in my other post http://www.gamedev.n...__fromsearch__1)

Ahhh yes. Don't do that, I assumed you would be loading only the stuff you needed. You want to load your resources as you need them (As you have correctly pointed out). So if your level needs 10 textures load 10 textures. I have posted some information on your other thread.

You don't want to pass the context to onDrawFrame the interface won't let you. What you can do is pass the context into the Renderer so that it is available for use elsewhere.

This might get a bit long and so I apologise if i am being too verbose and at too simple a level. Also I am not making any claims that this is the only way to do things.

You have your Activity (Generally you will have an activity for the game, and 1 or more activities for menu screens and so forth). Moving between your activities is a place where you can decide to do some loading of the things you need for the next activity and free up any memory (If this is going to be a time consuming process you display a lightweight loading screen while the loading takes place).

In your activity for your game I am assuming you will have your GlSurfaceView and your GlRenderer of some kind. The surface view as far as I recall has a setRenderer method that allows you to inject the renderer you want to use. The GlRenderer exposes the onDrawFrame(GL10 gl) method which will get called constantly by the surfaceView. It sounds like your onDrawFrame(GL10 gl) looks a little like the below.

public void onDrawFrame(GL10 gl) { endTime = System.currentTimeMillis(); long dt = endTime - startTime; int fps = Math.round(1000f / dt); if (fps < TargetFps) { try { Thread.sleep(TargetFps - fps); } catch (InterruptedException e) {} } startTime = System.currentTimeMillis(); // update the game state // render the game using the gl context }

So your question is how do I get my Bitmaps into memory such that my Level has access to them?

I would very much recommend using a class purely for loading the bitmaps (This can be a Singleton but you have to be careful about how you work with a Singleton in Android as your application can be killed at anytime and thus your Singleton has to rebuild its state when the app resumes. For managers of things like bitmaps and sounds this generally is not a big deal and easily handled.).

You could possibly do something like the below if you use a singleton style object for handling your bitmap management.

// This is just off the cuff code, i don't expect it to compile it's just to give you an idea. public class GameActivity extends Activity { @Override public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); MyGameLevel level = createTheLevel(); GlSurfaceView view = // Get a hold of your view or create it Renderer r = new LevelRenderer(level); view.setRenderer(r); // If this causes noticeable issues on starting the activity don't do it Runtime.getRuntime().gc(); } private MyGameLevel createTheLevel() { // Create a level it can load the textures MyGameLevel level = new MyGameLevel(); // Load all the textures i need (this could be done by the level class this is just an example for arguments sake) MyTextureManager.instance().loadBitmap(context, R.drawable.whatever); MySoundManager.instance().loadSound(context, R.raw.sound1); // etc...but all your resources are loaded } } public class LevelRenderer extends Renderer { MyGameLevel level; public LevelRenderer(MyGameLevel l) { level = l; } @Override public void onDrawFrame(GL10 gl) { // Game loop level.update(dt); level.render(gl); } @Override public void onSurfaceCreated(GL10 gl, EGLConfig config) { // You can do this or you can wait until you call level.render before binding the textures, your implementation will suit your needs level.bindGlTextures(gl, config); } } public class MyGameLevel() { public void bindGlTextures(Gl10 gl, EGLConfig config) { // here you can use the MyTextureManager to get the bitmaps and bind them to your gl context as you call this as soon as the surface is // created...if you wan't to, delay this until the render call it is fine the important thing is the Textures exist already in the manager in bitmap form. // something like ....can't quite remember the syntax gl.bindTexture(GL_TEXTURE_2D, MyTextureManager.instance().getBitmap(R.drawable.whatever); } public void render(GL10 gl) { // draw the level } public void update(long dt) { // update the level } }

There are many ways to achieve the same thing so what I have posted above is one method for loading the Bitmaps and providing access to them. In the onfinish method of the activity you can clear the texture manager and recycle any bitmaps you no longer need. Does this help at all?

I realised I didn't post any code for the MyTextureManager class I made up in the example above...however I think you can get the idea. You are just using that as a cache to store your Bitmaps. The method to load a bitmap takes the context which is readily available in the Activity (where I used it in my example..but it can be wherever you pass the context...you could for example call getApplicationContext() in your Activity and pass that context into the Level and have the Level manage putting the Bitmaps it needs into the MyTextureManager) and a resource ID. Thus you would just call BitmapFactory.decodeResource(context.getResources(), resourceId). Obviously how you architect things will be suited to your needs I am just proposing ideas to get you thinking.

Share on other sites
Many thanks Fiallen

This looks very promising.

I will do some tests based upon your suggestions through this weekend and then let you know how did it go.

Share on other sites
The method to load a bitmap takes the context which is readily available in the Activity.

Of course !!! Sometime you are looking for a solution and cannot see that which is right under your nose.

It is because I program in Android / Java environment only a couple of months and still cannot get used to some of its quirks.

I am now loading the level-dependent bitmaps via functions in Activity, but I am calling these from my main game loop which is in onDrawFrame.

I did not use your approach of writing the dedicated class for handling the bitmaps (though it may be neater solution) as my game is now 99% finished and this would mean substantial rewriting of everything and further delays with finishing the game.

Application still struggles at the startup, going as low as 1% of the available memory (as in above log), but then recovers and increases to some 74% - that with 95% of all bitmaps required for the game already in. I am a bit concerned about this startup struggling, but I have run it yesterday about 50 times on the actual device and it did not go OOM a single time, so I guess this is some inherent feature of Android.

Thank you for all your help and patience Fiallen !

Share on other sites
No worries at all, glad you got it working.

So longs the heap increases in size you will be fine (it may report 1% memory free and then up the heap size). Your issue was coming from the memory usage being at 48Megs and the virtual machine being unable to up the heap size any further. If you have it loading the bitmaps you need on a per level basis I doubt you will get anywhere near the 48Meg limit .

I'd give it a try on various devices though just to be absolutely certain, but it does sound like you have solved your problem, well done.