Each app process is forked from an existing process called Zygote. The Zygote process starts when the system boots and loads common framework code and resources (such as activity themes).
To start a new app process, the system forks the Zygote process then loads and runs the app’s code in the new process. This allows most of the RAM pages allocated for framework code and resources to be shared across all app processes.
Most static data is mmapped into a process. This not only allows that same data to be shared between processes but also allows it to be paged out when needed.
eg. Dalvik code (pre-linked .odex file), app resources, traditional project elements – native code in .so files.
In many places, Android shares the same dynamic RAM across processes using explicitly allocated shared memory regions (either with ashmem or gralloc).
Stack architecture for threads
Dalvik had separate stacks for native and Java code, with a default Java stack size of 32KB and a default native stack size of 1MB. ART has a unified stack for better locality. Ordinarily, the ART Thread stack size should be approximately the same as for Dalvik.
A Thread is a concurrent unit of execution. It has its own call stack for methods being invoked, their arguments and local variables. Each application has at least one thread running when it is started, the main thread, in the main ThreadGroup. The runtime keeps its own threads in the system thread group.
Dalvik uses registers as primary units of data storage instead of the stack. Google is hoping to accomplish 30 percent fewer instructions as a result.
Stack size for threads
The OS allocates the stack for each system-level thread when the thread is created. When the thread exits the stack is reclaimed. The size of the stack is set when a thread is created.
Android’s default stack size is 8KB. This gets you 60-100 stack frames, depending on how complex your methods are.
There appears to be a separate thread (called the HeapWorker) in each VM process that performs the garbage collection actions.
- When the Dalvik heap hits a soft occupancy limit. VM Parameter – InitiatingHeapOccupancyPercent. Default value is 45%.
- When memory allocation for an object fails.
- Just before triggering OutOfMemoryError – full, synchronous garbage collection is done.
- Explicit triggering by calling System.gc()
Android does not defragment the heap to close up space. Android can only shrink the logical heap size when there is unused space at the end of the heap. But this doesn’t mean the physical memory used by the heap can’t shrink. After garbage collection, Dalvik walks the heap and finds unused pages, then returns those pages to the kernel using madvise. (Reclaiming memory from small allocations can be much less efficient because the page used for a small allocation may still be shared with something else that has not yet been freed).
Android Memory Management
Android does not offer swap space for memory, but it does use paging and memory-mapping (mmapping) to manage memory.
This means that any memory you modify—whether by allocating new objects or touching mmapped pages—remains resident in RAM and cannot be paged out. So the only way to completely release memory from your app is to release object references you may be holding, making the memory available to the garbage collector.
That is with one exception: any files mmapped in without modification, such as code, can be paged out of RAM if the system wants to use that memory elsewhere.
Heap Memory Management
Each Android application runs in a separate process within its own Dalvik instance, relinquishing all responsibility for memory and process management to the Android run time, which stops and kills processes as necessary to manage resources.
Why we have a heap: While allocation may be slower than a stack (O(log n) vs O(1)), heaps allow freeing memory at an arbitrary location to be fast – O(log n), compared to a stack’s O(n).
What is Binder?
Binder is an Android-specific mode of achieving IPC (inter-process communication). It is a high-level implementation spanning several layers of the Android software stack. The main implementation can be found in binder.c in the kernel source code.
One Android process can call a routine in another Android process, using binder to identify the method to invoke and pass the arguments between processes.
Some functionalities provided by the Binder IPC are:
- Link of death
Java interface to Binder. Inherited constants – From interface android.os.IBinder.
Public constructor – Binder().
Garbage Collection (GC)
Automatic garbage collection solves problems of explicit memory management – dangling references and space leaks.
- Reference counting
- Tracing algorithms – tri-colour marking scheme (white, black, grey)
- Mark and sweep
GC pointer goes on checking for the live objects and if found it sweeps to the left side of the heap so that a contiguous space is formed.
Avoids repeated collection of objects by dividing the heap into old and young generations. Monitors references from old to young – “Remembered set” – use as roots to collect just young space.
A method of performing GC using semi-spaces, that is, by splitting heap memory into two parts and only using one at a time.
Concurrent Mark-Sweep (CMS) collector
Collection of the old generation is done concurrently with the execution of the application. Initially marks all the live objects.
When a thread gets blocked in the kernel:
When a thread does something that may cause it to become blocked locally, for example, waiting for another thread in its process to complete some work, it calls a run-time system procedure. This procedure checks to see if the thread must be put into blocked state. If so, it stores the thread’s registers (i.e., its own) in the thread table, looks in the table for a ready thread to run, and reloads the machine registers with the new thread’s saved values.
If there are no available threads to run:
Execute the idle thread – try to conserve power and have a low exit latency (i.e. sit in a loop waiting for a thread).
Idle thread defined as: void cpu_idle(void);
Android Rule for updating UI:
- Do not block the UI thread (which runs an infinite loop and checks a queue to see if there are any pending UI events) – else ANR (application not responding) dialogue.
- Do not access the Android UI toolkit from outside the UI thread.