WildBill's Blogdom

Mongo only pawn, in game of life.

Mac OS X Memory Management: You Kinda Suck, Yo.

| Comments

Even though I write for Linux Journal, I’ve made no secret that I use Apple’s OS X as my laptop/desktop OS choice. This angers a lot of people, and I’ve even heard a semidrunken near-hour long rant about how I was the “worst person ever” for doing so. (To the ranter: You know who you are, and that’s still one of the funniest hours in recent memory. My main reason for using OS X is similar to Miguel de Icaza’s, in that it gets out of your way and lets you work. While its fun to dig into the internals of a system and fiddle and figure things out from time to time, its not something I need to do in the middle of a work day when I’m pressured to do tasks that directly impact our product’s release schedule. Hence the move to OS X on the desktop. Whatever. I’m off topic, so I’m going to veer back to the topic at hand: OS X’s memory management.

I’m going to go out on a bit of a limb here and state that OS X’s memory manager is just a little bit schizophrenic, and quite possibly out and out insane. What do I mean by this? Well, I’m going to have to explain a little bit about how Linux’s memory manager works first, as I think that’s a “sane” implementation. (Whether or not it really is, is open to debate.) Below is an example of the “free” command’s output in Linux.

bill@miranda:~$ free total used free shared buffers cached Mem: 2053032 1916744 136288 0 252540 323288 -/+ buffers/cache: 1340916 712116 Swap: 4016212 1000016 3016196

What does this mean? Well, to understand that, we need to understand the different categorizations of memory listed above. The “Mem” row refers to actual physical memory, and the “Swap” row refers to virtual memory in the form of a swap file or partition on the hard disk. Of course, you want the system to avoid swapping if it can help it, as the increased I/O activity by thrashing the hard disk will bring the performance of the machine way down. The columns refer to what state that memory is in. So, the machine has 2GB of physical RAM, of which 1.9GB is used, and 136MB are available to be allocated but that’s sort of misleading. It’s misleading because Linux has an in-RAM file cache where it stuffs files that are frequently accessed, so often-used file access is nice and fast. That’s represented by the buffers and cached columns in the listing above (I’m oversimplfying here, because once again, I’m off topic.) The in-RAM file cache is dynamic, though, and the kernel can grow or shrink that based on the memory demand of the system. So, the answer to the question of “How much RAM is available for use in this system?” is really the number in the center of the output: 712MB.

bill@miranda:~$ free total used free shared buffers cached Mem: 2053032 1916744 136288 0 252540 323288 -/+ buffers/cache: 1340916 712116 Swap: 4016212 1000016 3016196

Again, Linux dynamically adjusts the file cache based on system load and a few other variables – and you have at knobs like the “swappiness” setting that allow you to control the tendency for the system to swap to the disk for virtual memory. Let’s contrast that with OS X.

OS X is designed to be a desktop OS. That means the workload is typical desktop applications: mail, web browsing, word processing, photo editing, etc. A desktop user’s workload differs from a power user or developer. A desktop user might open a mail program, send an email, then close email and open a word processor for a bit, then close the word processor and re-open email you get the idea. Every one of those programs might request a couple hundred megs of RAM from the OS, use it, and then relinquish that back to the OS. OS X is optimized for just this case. It has the concept of “inactive memory”. Basically, when a program shuts down, the system will block that memory as “inactive”, so that if the user fires the same program up again, the program can start up extremely quickly. That’s fine and dandy for small memory blocks, but a developer’s workload generally is to use huge blocks of RAM at a whack. A developer might open a VM that consumes a couple of gigabytes of RAM, then close it, then compile a big program that might consume a huge whack for memory and as OS X marks those huge tracts of memory to be “Inactive”, your system looks like this:

Before Purge

Do a few more things, and your system will be OUT of physical memory and start to swap, and you’ll get the dreaded “beachball”. About this time is when you’re head desking and force quitting apps to try to get the system to be responsive. Fortunately, there’s a couple of workarounds, and maybe even a permanent fix.

The first workaround is to run the command “purge” in a terminal window when your system looks like it may be getting into trouble. Purge, at a high level, will reap inactive memory and push it back to the system so it’s available. Here’s a shot of the Activity Manager in OS X immediately after I ran the purge command:

After Purge

Note that where I had 480MB of RAM before, I now have 4.04GB available to the system. I’ve also avoided a beachball scenario. Happy Bill is Happy.

The second workaround is to install a wrapper around the purge command. There’s a great free little program called FreeMemory in the Mac App Store. FreeMemory will sit in your menu bar and display the amount of Free RAM – and when you get low, you can simply click on it and hit “Free Memory” and it’ll run the purge command under the hood. No muss, no fuss. I suppose you could run “purge” in a cron job if you were really nutso about this, but if you’re doing that you may as well go for a more “permanent” fix…

Turns out you can totally disable the dynamic pager in OS X. Not sure if this is a good idea, but if you’ve got 8GB of RAM or more it seems like this is a viable thing to try. It’s easy to do and easily reversible. To disable the dynamic pager, run the following command from a terminal and reboot:

sudo launchctl unload -w /System/Library/LaunchDaemons/com.apple.dynamic_pager.plist

If you want to re-enable the pager due to some bizarre behavior, running the following command in a terminal and rebooting will do that:

sudo launchctl load -wF /System/Library/LaunchDaemons/com.apple.dynamic_pager.plist

So, give these things a try, and see if your system responsiveness gets better. The “purge” command’s come in handy for me so far, but I have yet to be brave enough to disable the dynamic pager. If you DO try it - let me know in the comments how that’s worked for you!