I love developing for the iPhone. It’s a really fun machine. Small enough to allow very small teams to create great apps, but at the same time powerful enough that you can do some really impressive games. The tools are great, the iteration time is great. It’s was a pleasure all around developing Flower Garden.
Except when it comes to memory, that is.
I really like most of the design calls that Apple made designing the software for the iPhone: Using C and Objective C (instead of Javascript or web-only apps), building it on top of a Unix-like core, and even providing OpenGL (although I would love to have lower-level access to the graphics hardware).
Developing for the iPhone is a mix of PC and game console development. One one side, it’s a fairly open platform with standard APIs (like PCs), and on the other, it has (almost) fixed hardware [1] and a regulated distribution channel (like game consoles). I would even argue that the iPhone borrows the best of both worlds. When it comes to memory, however, the iPhone adopted the worst of both worlds. I feel that Apple completely dropped the ball there. Completely.
On a PC (whether it’s Windows, Mac, Linux, or whatever), you can’t count on having a fixed amount of memory. When your program runs, it can be running in a machine with RAM to spare, or in one with barely enough to run the OS. Or maybe it’s running on a machine with enough RAM but there are fifty other programs running and there’s no memory left for your program.
Fortunately (or unfortunately depending on your point of view), virtual memory allows the operating system to write memory pages back to disk and free up space for your program. Yes, it can cause the program to halt and crawl while memory is being paged out, but it will run. Definitely not an ideal solution for semi-real time applications like games. That’s one of the main causes for choppy, underperforming PC games, but it has worked for many years and will do in a pinch.
On consoles on the other hand, you have a fixed amount of RAM from the start. You know what that amount is, and you can plan for it. In this latest generation of consoles, you still need to give up part of that precious memory to some other processes for user interface and network updates, but even that amount is known ahead of time. You can plan for it, and make your game fit in the memory that you have left.
The iPhone memory situation is… a mess. It’s like a console in that you have a fixed amount of RAM: 128 MB (although I’m sure Apple doesn’t want developers thinking about it so they can roll out iPhones with more memory without affecting existing programs). Because it has a fixed amount of RAM, the iPhone doesn’t provide virtual memory swapping [2]. So far so good. But here’s the kicker: Unlike a game console, the iPhone makes no guarantees about how much memory your application has available when it runs.
Let me say that again: You’re dealing with a fixed-memory environment and you have no way of knowing how much memory will be available to you. Did that sink in yet?
So how are you supposed to deal with that? Apple wants us to be able to dynamically load and unload anything in our programs in response to low-memory warning events. That might work well if you’re loading web pages and just need to unload some cached ones, but that’s far from ideal for games. What are you going to do? Unload part of the level? Get rid of some sounds? Not every game can do that, and even if you could, it would add a huge amount of complexity to something that should be pretty simple.
You can’t even plan on using a fixed minimum amount of memory. From my experiments on my iPhone 3G, it seems that it’s relatively safe to use between 15 to 18MB. Any more than that, and you start getting low memory warnings, and unless you program does something about it, the OS will promptly terminate you. On an iPod Touch, since it’s not running as many background services, you usually have more memory available. I learned that the hard way, because I developed most of Flower Garden on an iPod Touch, only to find out that it was too slow and running out of memory on the iPhone 3G.
And this is out of a total of 128MB. Where did the rest of the memory go? Come on, even with phone, GPS, and music processes in the background, where did the rest of the memory go? Probably the worse culprits are the mail program and Safari, which are left in memory even after you exit back to the springboard (Bad design decision, no cookie!).
And what exactly does it mean “to do something about it” when you get a low-memory warning? You’re supposed to free up as much memory as you can, but is it enough? Who knows! Maybe you free up one megabyte but the OS still decides that you’re taking too much memory and it will kill you anyway. It seems like the worst memory handling scheme ever designed.
As an extreme situation, I have seen as little as 3.5MB available when starting my own program! What are you supposed to do in that case? Most games will just crash back to the springboard. Want to test this? Launch Safari, load 10-15 heavy web pages, quit, and then launch your favorite game. A crash is almost guaranteed. For my next project, I’ll be tempted to respond to a low-memory warning by killing the Safari and Mail processes. That will free up some memory!
To make matters worse, I had the bright idea of combining OpenGL and UIKit for Flower Garden. Some things worked really well, like being able to quickly create some screens with Interface Builder and take advantage of all of Apple’s UI widgets and fonts. Creating the seeds screen or the settings panel was a breeze with UIKit. However, UIKit uses an undetermined amount of memory behind the scenes, which makes all attempts at staying below a certain memory threshold pure black magic. In particular, drawing images to a context apparently causes mysterious caching of bitmaps taking huge performance hits and memory chunks over which we have no control. And game programmers don’t like to feel they have no control over what’s happening!
Another consequence of such a horrible memory situation is that game developers aren’t able to make full use of the resources available. If we’re aiming for 15MB, but have to drop back to 5-8MB, most developers are not going to try to write a game that uses 30 or 40MB (which might be available). What a shame!
I’ve said before that the iPhone very closely resembles the great Sega Dreamcast (which is my favorite game console of all time). The Dreamcast only had 16MB of main RAM and 8MB of video RAM, but the best games on the iPhone, still pale in comparison to the best Dreamcast games. Why? Mostly because of the memory situation. If we had a guaranteed 16MB of RAM and the possibility of using all the extra free memory, iPhone games would look very, very differently.
My plea to Apple: Please, give us some minimum memory guarantees for OS3.0! That, more than anything else, will make the biggest difference in the look and reliability of future games.
[1] There are slight differences between iPhone 2G, 3G, iPod Touch 1st and 2nd gen (especially the latter, which has a faster CPU and faster file system).
[2] It has virtual memory in the sense that there’s a paging system with virtual addressing (so two memory pages far apart in real memory could be made to seem to be continuous), but there is no automatic paging in and out of memory pages to/from disk.
maybe you *should* kill the safari and mail processes 🙂 that’s what the app-store-approved “Memory Status” app does, which i regularly run before launching any graphics-intensive app. i wonder if apple would allow that in a game…
Incredibly insightful into the software limitations of iPhone development. Mid way through the read I began to wonder about an architecture that would allow the scaling back of memory dynamically up to the point of an alert advising the user to close some safari tabs (I actually did not realize this was a continuous service). But I do agree this is the sort of added code complexity that has no place in the actual game development process where hardware is predictable.
I believe that, from what I’ve read, you have about 64 MB of memory available for your application, 64 MB being taken by the OS. Of that 64 MB, you have to account for 24 MB of it being shared for the PowerVR. Our applications are going well into 30 some MB and play quite fine.
I’m not quite sure exactly how your programs are managing their assets, but you have to be very careful to build the system in such a fashion that memory is only repeated once, ever, and re-used. In terms of objects then, it becomes an issue of normalization – if you have a texture being used on one object, don’t re-load the same texture just because it needs to be used for a different one. GLES may make it a bit easy with it’s texture ID numbers, but it still should be treated as a shared resource. Another aspect is sounds, which, if you are using a long sound, make sure that you are using the interfaces that do streaming and not just load the entire sound into memory. If using OpenAL, using the alStaticBufferData() extension is a must, which lets one manage their own buffer memory instead of using alBufferData() that does a memcpy().
Using the UIKit on top of a GLES rendering view is also problematic because of the buffer swap having to sync with the UIKit buffer swap. I believe I’ve read a good deal of documentation that suggest to take over the entire screen and avoid any overlays, especially with GLES content overlaying UIKit content. A good discussion this is at: http://developer.apple.com/iphone/library/technotes/tn2008/tn2230.html
As far as paging is concerned, it would be an awesome leap forward for the OS 3.0 to have paging that swapped in and out of the on-board storage (remember, paging doesn’t have to be with a disk, it can be any storage medium). Also given that the on-board memory is solid state, I personally would not be surprised to see the mechanism itself be quite efficient. I think, mainly, it’s a matter of time and money. Apple needs to get a return on its investment into adding new features, so, as long as they keep making money, there shouldn’t be any reason why they don’t continue to add onto their existing platform.
Best of luck.
Indeed, memory handling is real pita on iphone. However most times you can force mail and safari into low memory situations and such free the ram by force. Huh – how to do that? I experienced a lot of mem related issues in one of my commercial solutions and started searching for a more or less usefull solution. There are some thinks I discovered in that progress:
1. Allocating a large chunk of memory can kill your app really fast.
2. Allocating and using the memory are different thinks. Memory you allocate is not always your memory. You can sometimes happily allocate 128mb of raw bytes via malloc and not getting kicked for it, but trying to access parts of that chung an hasta la vista application…
The bottom line is iPhone OS is not nice to you when it comes to memory handling. However, I use roughly following to get what I want.
During startup, spawn a thread (a real one), which:
1. Allocates memory in little chunks. I use 512k chunhs of chars.
2. Between all the allocs, sleep a few millis.
3. If you got your memory, use it. I write some randomly random chars into a block.
4. Better sleep a few millis between each block.
5. Look at your Instruments, hopefully you see what I mostly saw: Mail and Safari got killed/sized down.
6. Slowly release the blocks you allocated.
I do this, while playing an intro movie. The Movie sucks about 7 MB and leaks afterwards around 2MB. The memory I free is about 20MB and lots of testing showed cleary that I’m able to use about 25MB of memory during the excecution.
One hardcore test is to reboot your iPhone, open Mail, open a mail with a at leats 5 mb powerpoint slideshow, watch the whole slideshow, return to another mail, with a large xsl attachement, go to a third mail with an embedded link. Open the link and open the maximum possible amount of webpages (best ones are the real big ones, with lots of javascript). Then close Safari and try to start your app. Good luck! 90% of all App I tested fail at this point.
Sorry for my bad english.
That’s a pretty clever idea, fishermen21! I’m going to experiment with it!
Why not build your own heap class, and at the very start of your game, allocate the max you’ll ever need (say, 20mb or whatever). For the purposes of your game, only ever alloc/free from that custom heap.
A couple of benefits to this approach:
– You can, at startup, immediately see whether or not you have enough memory available for your game. If you don’t, either try scrubbing memory, or inform the user to reboot.
– Tighter control of allocations, memory leaks, and so on. Activity inside of that heap is specific to your game, and so you have the maximum amount of control possible.
– Less chance of running out of memory midgame by way of fragmentation from external parties, assuming you were able to alloc the initial 20mb chunk or whatever. Basically, if you were able to alloc your heap, you’re 99% guaranteed to run your game without a hitch (memory-wise).
– Makes porting the game to other platforms a little easier – since you’d just have to worry about where the initial mem alloc comes from. Everything else remains the same.
How lame of Apple. There should be a special game mode that swaps everything to disk so that you can just use all the memory.
And Fisherman, you don’t have to apologize for your English. It’s easily above average.
Graf, I wish I could just allocate all the memory up front. That’s the way I prefer to do it, but there are problems with that. You need to wait for other processes to free memory in order to get your memory, which could be a while (how long? No idea, need to time it). Also, background processes might need more memory all of a sudden (incoming phone call), so you might still be forced to free memory.
I definitely want to experiment with a lot of the suggestions so far. Some of these ways allow us to get around some of the memory problems, or at least make them more predictable. I still wish that Apple could just guarantee a minimum amount of memory though. It would make life a LOT easier!
Our company is getting into iPhone development. At this point it’s only been prep and research, but we will be hitting it full board soon.
This information is good and will be something for which I now know to watch out. Great post. Thanks.
One other thing that affects the amount of memory you have available is when a user has jail broken their handset. If it’s also customised with weird versions of SpringBoard you can get up to 10Mb less than a stock iPhone — and the users complain when your app doesn’t work properly!
No convenient answers I’m afraid. I wrote about this a little more on my blog, but I definitely agree that one of the nice things about programming for the iPhone is that you’re not working with a moving target.
There actually is a virtual memory system pager. However you have to manually set it up.
Search for ‘mmap’ in Apple’s iPhone Developer Forum and you’ll find a very informative thread.
Basically, it works like this: In place of malloc, you create a file of the appropriate size and then memory map that file using mmap. The virtual memory pager will page in and out the memory pages as needed. This works both for read-only data (you can just use [NSData dataWithContentsOfMappedFile: ] for that) as well as read-write memory.
I noticed that the Monopoly app gives a warning pretty much every time I run it that it recommends me to reboot my phone. I remember another wild west shooter that did the same. It’s a solution, but I admit not a very elegant one. I also really hope Apple fixes this in 3.0. I can also imagine that many 1-star app reviews have come from apps crashing on iPhones that were dangerously low on memory.
@Chuck – I actually experimented with that in Dapple during Alpha/Beta testing. The problem was that as soon as that popup comes up, the user immediately thinks that it’s something your app has done wrong, not the phone. I got a bunch of emails from testers after I put that in saying “I think *your app* broke…I got a warning telling me to reboot my phone” (emphasis mine). The problem being that they blame the app, not the OS. It’s a tricky problem to deal with; you want to provide the user with information, but not have them think it’s your fault. 😉
I’m not an OS programmer nor a dev. , but I’m just wondering why isn’t there an appication for the iPhone that can solve this issue like the Paging file in the Windows OS? On the smallest iPhones there are 8GB of HDD memory. With the OS on them there still are more than 6.5 GB of space. Why not create a program that allows ppl to allocate an amount of virtual memory to the OS? 64, 128 or 256 extra MB would solve all those problems users/devs are facing with.
Sorry if I’m talking nonsense for you guys, but as I said, I’m not a connaisseur .
Valentin, you should read my next post on memory and virtual memory systems 🙂
Apple engineer “eskimo1” wrote a detailed reply about the iPhone’s virtual memory system on the Apple Developer Forums: https://devforums.apple.com/message/49187#49187