Cover source: medium.com/android-new…
preface
Nice to meet you
Memory optimization has always been a very important topic in Android development, which directly affects the performance of our app. But the topic is so broad and low level that many developers recoiled. At the same time, memory optimization is more inclined to “empirical knowledge”, which needs to be applied in the actual project to learn.
So this article doesn’t want to dive into the basics of memory optimization, but rather look at the big picture and talk about how Android allocates and manages memory, what the system does when it runs out of memory, and how it affects users.
Android applications are developed based on THE JVM language. Although Google has developed its own virtual machines such as Dalvik and ART according to the characteristics of mobile devices, they are still based on the JVM model and allocate object memory in the heap. Therefore, The Java heap is the focus of memory allocation and reclamation for Android applications. Secondly, mobile devices have very limited RAM, so how to allocate and manage memory for processes is also important.
The article focuses on analyzing The Java Heap, memory management of RAM, and what Android does when it runs out of memory.
So, let’s get started.
Java Heap
The Java Heap, or Heap area in the JVM. A quick review of the division of runtime data areas in the JVM:
- The method stack and program counters in the orange area are thread private and store local data in the method.
- The method area mainly stores constants and class information, which is shared by threads.
- The heap is responsible for storing objects that are created, and the memory of almost all objects is allocated in the heap, which is also shared by threads.
Objects created in Android programs using code such as Object O = new Object() will be allocated a chunk of memory in the heap for storage, and how to allocate the memory is left up to the virtual machine without our developer intervention. When an object is no longer in use, the JVM has a garbage collection (GC) mechanism that automatically releases unwanted objects from the heap and reuses memory. An OOM exception will be raised if the memory allocated exceeds the size of the heap.
In Android, the heap is an area that is logically divided by the JVM; it is not really a physical area. The heap does not directly map all of its physical memory, but only maps logical and physical addresses when needed:
This allows you to allocate enough logical memory for your application without having to allocate a large chunk of physical memory at startup. More programs can be run in the same amount of memory.
After the heap process GC, the excess free memory is returned to the system to reduce the occupation of physical memory. However, this process involves complex system calls. If a small amount of memory is released, the gain may outweigh the loss. Therefore, it is not necessary to return the memory to the system and continue to use it in the heap.
During GC, if an object is no longer in use but its memory cannot be released, resulting in a waste of resources, this phenomenon is called a memory leak. Memory leaks can lead to more objects in the heap, more pressure on memory, and even OOM. Therefore, memory leaks are a phenomenon we must avoid as much as possible.
Process memory allocation
Memory allocation of the heap is an internal memory allocation managed by the process itself. Let’s talk about an application and how the system allocates memory for it.
The system’s running memory, also known as RAM, is the running space of applications. Each application must be loaded into memory before it can be executed:
- The application processes we install are all on the hard drive
- When an application is being executed, it needs to be loaded into RAM to be executed (zRAM is designed to compress data and save space, which will be covered later).
- The CPU interacts with RAM, reading instructions, data, writing data, and so on
The size of RAM is the size of the hardware memory of the device and is a very valuable resource. Modern phones typically have 6GB, 8GB or 12GB of storage, and some phones developed specifically for gaming have even 18GB, but the price will follow.
Android uses paging storage to store a process in RAM. Paging storage, in simple terms, is the partitioning of memory into many small pieces, each application occupies a different small piece, these small pieces can also be called pages:
As mentioned earlier, the heap area of a process is not allocated all at once. When memory needs to be allocated, it is allocated free pages. When these pages are recycled, they are likely to be returned to the system.
The concept of pages and blocks refers to paging storage in operating systems. It is not intended to be explained in detail here, but interested readers can find out for themselves: Paging storage – Wikipedia. In this article, “page” and “block” can be loosely understood as the same concept, and a detailed distinction is not made here for the sake of understanding.
Pages assigned to processes can be divided into two types: clean pages and dirty pages:
- Clean page: Not modified after a process reads data from disk or allocates memory. This type of page can be reclaimed when memory is low, because the data stored in the page can be recovered in other ways.
- Dirty page: A process has modified or stored data in the page. Such pages cannot be reclaimed directly. Otherwise, data is lost. You must store the data first.
ZRAM, as a partition in RAM, can be compressed to store some types of pages in zRAM when memory is low, and then retrieved from zRAM when needed. Compression saves application space, eliminates the need to schedule data with hard disks, and improves application speed.
One thing to understand here is that operations in memory are much faster than operations on hard disk. Even though calling in and calling out of zRAM requires compression and decompression, it is much faster than interacting with a hard disk.
Insufficient Memory Management
As we’ve emphasized above, the memory capacity of mobile devices is very limited and needs to be used very carefully. Fortunately, the JVM and Android have long helped us figure this out.
Android has different strategies for dealing with different memory pressures. From lowest to highest, GC, kernel swap daemons free memory, and low memory termination daemons kill processes to free memory. Their costs are rising. Let’s follow one to introduce it.
GC garbage collection
The GC is the internal memory management mechanism of the JVM, and the area of memory it manages is the heap. As more objects are created and the heap becomes more stressed, the GC mechanism starts to collect garbage objects from the heap.
Virtual machines use reachability analysis to determine whether an object is garbage. That is, starting from some identified active and useful objects, the downward analysis of his reference chain; If an object is referenced directly or indirectly by these objects, it is not garbage, otherwise it is garbage. These objects identified as active and useful are called GC Roots:
- As shown in the figure above, green objects referenced directly or indirectly by GC Roots will not be recycled. Gray objects that are not referenced are marked as garbage
The more common types of GC Roots objects are static variables and references in the stack. Static variables are easier to understand because they are not recycled throughout the execution of the process, so they are definitely useful. The stack, by which I mean the method stack in the JVM’s run data area, which is the local variable reference, is definitely active during method execution. Since the method stack is thread private, objects held by the active thread are not reclaimed.
Therefore, if an object is no longer used by our program, it must be unreferenced by GC Roots, otherwise it will cause a memory leak. For example, do not assign an activity to a static variable, which will result in the activity not being reclaimed when the interface exits.
Instead of collecting the whole heap directly, GC divides the objects in the heap into two parts: the new generation and the old generation.
Newly created objects are mostly recycled, and objects that survive multiple collections are rarely recycled. The new generation stores mostly newly created objects, while the old generation stores objects that have survived multiple GC’s. We can perform different collection algorithms for these objects to improve GC performance:
- For newly created objects, we need to GC them more frequently to free memory, and only record the objects that need to be left at a time, rather than marking a large number of other objects that need to be reclaimed, improving performance.
- For objects that have survived many GC’s, they can be GC’s at a lower frequency and only focus on a small number of objects that need to be collected at a time.
The specific garbage collection algorithm will not continue to expand, understand here can be. Interested readers can check out the garbage collection article or read a book about it.
The single garbage collection is so fast that we don’t even notice it. However, as memory pressure increases, garbage collection can’t keep up with the speed of memory allocation, and memory allocation waits for GC, which is a holdup. At the same time, we have no control over the timing of GC, and the JVM has a whole set of algorithms to decide when to GC. If GC is triggered while we are sliding the screen, we are showing a frame drop. Therefore, memory optimization is very important for app performance.
Kernel swap daemon
GC is an optimization for internal Java programs. For mobile devices, RAM is very valuable, and how to allocate memory on the limited RAM resources is also a very important topic.
Our applications all run in RAM, and when the process continuously requests memory allocation and the remaining RAM reaches a certain threshold, the kernel swap daemon is started to free up memory to meet the resource allocation.
The kernel swap daemon is a process running in the kernel. Its main job is to recycle clean pages, compress pages and so on to free memory. As mentioned earlier, Android is a paged storage based operating system, where each process is stored in several pages. There are two types of pages: clean pages and dirty pages:
- When the kernel swap daemon starts, it recycles clean pages to free memory. When the process accesses the clean page again, it needs to go to hard disk to read it again.
- For dirty pages, the kernel swap daemon compresses them and places them in zRAM. When a process accesses dirty pages, it needs to extract them from zRAM.
Memory is freed up by continually reclaiming and compressing pages to meet new memory requests. When the memory freed in this way cannot meet new memory requests, Android starts the low-memory termination daemon to terminate some low-priority processes.
Low memory terminates the daemon
When the RAM usage reaches a certain threshold, Android terminates some processes according to their priorities to release memory. When the low memory termination daemon starts, it indicates that the system is already under a lot of memory stress, which is often the case on some poor performance devices.
Processes are terminated in descending order of priority. Processes with higher priorities are terminated first:
Image: developer. The android. Google. Cn/topic/perfo…
From top to bottom:
- Background apps: Used apps are cached in the background and can be switched more quickly the next time you open them. Such applications are killed fastest when memory runs out.
- Previous app: For example, when you jump from wechat to the browser, wechat is the previous app.
- Home screen app: This is the launcher app, which is our desktop. If the process is killed, it returns to the desktop with a temporary black screen.
- Services: synchronization services, upload services and so on
- Perceptible application: for example, music software is playing, which can be perceived by us, but not in the foreground.
- Foreground application: an application that is currently in use. If the application is killed, it needs to report a crash exception to the user. The experience is very poor.
- Persistence (services) : These are the core services of the device, such as telephony and WLAN.
- System: a system process. After these processes are terminated, the phone may be about to restart, just as the phone suddenly freezes and restarts.
- Native: A very low-level process used by the system, such as our kernel swap daemon.
When memory is insufficient, the process is terminated from top to bottom according to the preceding rules to obtain memory resources. That’s why our background apps keep getting killed on Android. In order to prevent our application from being optimized, memory optimization is very important.
One final review:
Image source: www.youtube.com/watch?v=w7K…
- In the 0-1 phase, the system has enough memory resources, the program requests memory allocation, and the system will constantly use free pages to satisfy the application’s memory requests
- During phase 1-2, the available memory of the system drops to a threshold, the program continues to request memory allocation, and the kernel swap daemon starts to free the cache to satisfy the memory request
- In phase 2-3, the system’s utilized memory reaches a threshold and the system starts a low-memory termination daemon to kill the process and free up memory
The last
We’ve looked at how Android allocates memory and how it frees up memory to meet memory requests when it’s low. Obviously, when the memory is insufficient, it will seriously affect the experience of our app or even the entire user’s mobile phone:
- Running out of memory causes frequent GC, clean page collection, and write back cache, resulting in slow applications and stuttering
- If the device is always out of memory, it will always kill processes and affect the user experience, especially those processes that users care about very much, such as games and wechat
- Too much memory will cause the APP to be killed in the background, or the user’s other apps to be killed, or even the whole system can not run and directly crash and restart.
- Not all devices have high memory, some devices have very little memory, and on some of the lower performance devices will not even work, so we lose the market for these devices
In contrast, many apps in China, such as QQ, T-Bao and IQY, run on my machine of three years ago, and there will be serious lag and ANR crash occasionally. However, when I tested youto, Tele, Twit and other apps, I found that there was almost no lag, and even the interface switch of youto, an app loaded with a large number of pictures and videos, was smooth. The experience of the two apps is vastly different.
This article does not cover how to optimize memory, because it is too broad and deep to cover in this article. The purpose of this article is simply to give the reader a sense of how Android manages memory and the consequences of running out of memory, and to get a sense of how important memory is.
If the article is helpful to you, I hope to leave a like to encourage the author ~
Full text here, the original is not easy, feel help can like collection comments forward. Any ideas are welcome in the comments section. If need to reprint please comment area or private letter to inform.
And welcome to my blog: Portal