[Samsung Galaxy Tab Plus] h2g2:
I decided to do some more research (and tinkering) of Linux with regards to memory management in Android OS and I will need to revise some of my earlier statements.

I'd like to clarify first that the Linux kernel on the Android OS has the memory_overcommit flag set to 1 (can be confirmed with cat /proc/sys/vm/memory_overcommit). This means always overcommit which essentially means that the kernel will never report out of memory, with or without a swapfile. Instead, all memory allocations are granted. (Previously, I said that the kernel can return an out of memory error with a swapfile).

Now, if the VMM doesn't have enough free pages to accomodate the allocation request, then it will look for pages to reclaim. In this context, pages can be in one of four states: unreclaimable (e.g. any kernel-mode allocations and pages marked as reserved or locked), swappable (user mode program data), syncable (user mode pages used for memory mapped file ops and OS level disk and file system caches), and discardable (unused/free()'d pages). (Previously, I oversimplified it and simply broke it down to "active" vs. "inactive")

Linux employs a sophisticated algorithm to determine where to start reclaiming memory. In general, the process starts with discardable pages before moving on to flushing caches and swapping - /proc/sys/vm/swappiness determines the balance, default 60, which defines whether priority is given to flushing caches (lower) or swapping out program data (higher).

Generally speaking, emphasis on swapping will improve overall system performance while emphasis on flushing caches will improve responsiveness of the foreground app.

Nota bene: Do not trust anyone who backs up claims of system-level improvement with a Quadrant or Linpack score.

I missed Garyd9's second point here that last go-around so I wanted to clarify that there is a protection mechanism within the Android Runtime to protect against apps with memory leaks. In /system/built.prop, there are two lines that define limits on how large an individual Dalvik VM heap can grow. On the GT7+, these are:

dalvik.vm.heapgrowthlimit=64m

dalvik.vm.heapsize=288m

An app with a memory leak will hit the 64m limit and force close. If the app with a memory leak has largeHeap=true defined in its manifest, it will hit 288m and force close.

Note that Android 2.3 and prior only define dalvik.vm.heapsize. For context, the G1 had a heapsize limit of 16m while the Nexus One had a heapsize limit of 32m. Changing this value will affect things like Quadrant and Linpack scores. See my NB above.

---------- Post added at 10:50 PM ---------- Previous post was at 10:14 PM ----------

Edit: another mea culpa

One more mea culpa. I previously said that there is no system/purpose built solution but in fact, there are.

The first is the limit on the Dalvik VM heapsize, which I mentioned earlier. Added to this is Dalvik's garbage collector, which actively looks for unreferenced memory within the heap to free up. There are some limits here, though. By all accounts I could find, Dalvik's GC is non-compacting, which means that it won't "defragment" memory. In the context of this discussion, this means that heap fragmentation limits how much memory you can free up. The app can re-use those fragments, though, so it's not a total waste.

The second is indirectly related to the configuration of the Linux kernel to always overcommit memory. If enough pages cannot be reclaimed to fulfil a memory allocation request, then the kernel moves to the oom_killer (Out of memory process killer). On the Android OS, Google added an additional kernel module, lowmemorykiller, to extend oom_killer's functionality.

I mentioned lowmemorykiller earlier in this thread but didn't really spell out its role in Android memory management. Linux's stock oom_killer employs a basic algorithm to determine what processes to kill but lowmemorykiller is far more tuned into how the Android OS works. In short, lowmemorykiller identifies six different process categories and a separate low memory threshold which must be passed before processes in said category become eligible to be killed. These categories, along with their thresholds on the GT7+, are:

FOREGROUND_APP 8192

VISIBLE_APP 10240

SECONDARY_SERVER 12288

HIDDEN_APP_MIN 14336

CONTENT_PROVIDER 16384

EMPTY_APP 20480

Note that the thresholds are defined in pages and each page = 4kb of memory. So the low memory threshold for an empty app is 80mb (20480 * 4kb). These apps are tweaked by the v6 supercharger script so you can find examples of what apps fall under these various categories on posts that deal with that particular script.

Back to the question of whether swapping is "better" than the Dalvik VM's heap constraints and GC and memory overcommit+oom_killer/lowmemory killer, there is no clear answer because swap vs. Android OS memory management aren't exactly at odds with one another.

The limits on Dalvik VM heap sizes and GC actually help to make swapping more efficient by by reducing the number of pages that need to be swapped out.

oom_killer/lowmemorykiller comes into play after page reclaiming (this process includes swapping) fails to free up enough space. The worst case scenario here is that you have a large but unneeded process that gets swapped out when it would have been easier just to kill it.

However, this should be tempered by:

1) Page reclaimation only needs to free up enough pages to fulfill the memory allocation request. This means that even if you have, say, a full 288m process sitting in memory (the largest possible, given the heap size limit on the GT7+), if only 8mb of that needs to be swapped out in order to achieve the desired number of free pages, that's all that gets swapped out. Remember that swaps happen a page at a time and a single page is 4k.

2) Even without a swapfile, page reclaimation involves cache flushing which still has a negative impact on performance.

That said, the limits specified in lowmemorykiller will need to be tweaked on an Android OS with swap enabled. In particular, EMPTY_APP and CONTENT_PROVIDER can have their thresholds raised to just above or just under the amount of swap space available. This way, you can have more control over what processes do get swapped out.

See this post for an example.