Official video translation, if need to be reproduced, please specify the author: Yuloran (t.cn/EGU6c76)

Speaker Introduction

Rechard Uhler, Android Runtime Development engineer. To facilitate writing, the author will summarize the content of the video from a first-person perspective.

Video address

preface

To optimize memory, you must have a good understanding of The Android memory management mechanism to ensure that your application will perform well on low-end phones. Different Memory types, including Shared Memory, Dex Memory, and GPU Memory, all affect the user experience.

I’ve spent the last three years working on a deeper understanding of Android app memory management. So why should App developers care about memory footprint? For me, it’s mainly the Android ecosystem. Faangs. If an Android app is poorly experienced on low-end devices (such as frequent delays), Original Equipment Manufacturers (Oems) will no longer produce such devices, leaving them out of the Android ecosystem.

This topic mainly discusses three aspects:

  • How the Android system works when memory is low

  • How do I evaluate application memory usage

  • How to reduce application memory usage

How the Android system works when memory is low

First, you need to introduce the concept of physical Memory and then introduce Android Low Memory Killer.

Physical memory

The physical memory of the device is divided into many 4KB pages. Different pages are used for different things:

The orange are used Pages, the yellow are cached Pages (data is backed up on disk, so Cache Pages can be recycled), and the green are free Pages.

The kswapd process used to reclaim Cached Pages:

This is a phone with two gigabytes of memory, and on the X-axis is time spent, and on the Y-axis is memory usage. As more applications are opened, the number of Used Pages increases and Cached Pages and Free Pages decrease. When Free Pages falls below the kSWAPd threshold, the Linux kernel reclaims Cached Pages through the KSWapd process. When the application accesses the contents of the Cached Pages again, it needs to reload them from disk. If the Cached Pages are too few, the device may crash:

So on Android we have a mechanism called Low Memory Killer that will be triggered when there are too few Cached Pages. It works by selectively killing a process based on its priority, freeing up all the resources that the process occupies to meet its memory allocation needs:

As shown in the figure above, a low-memory kill mechanism is triggered when Cached Pages fall below the LMK threshold.

LMK (Low Memory Killer)

If LMK kills processes that the user is interacting with or perceiving, this can lead to a very unfriendly user experience. So the Android SystemServer process maintains a list of process priorities that LMK uses to decide which process to kill first:

  • 5. Perceptible refers to processes that are not directly interactive with the user, such as music player processes that play music in the background.
  • Previous refers to the application process before switching to the current foreground application;
  • Cached refers to the Cached process. The Cached process can be a backward application process or an exited application process for quick switching between applications. So the Cached process is also the lowest priority:

As shown in the figure above, when the used memory exceeds the LMK threshold, LMK kills the process from the bottom of the Cached list. If the available memory still does not meet the allocation requirements, the process will be killed from the bottom up according to the priority shown in the above table until the Kill SystemServer process is prepared, which will cause the phone to restart.

So, you can imagine LMK on a low memory phone:

As shown in the figure above, LMK will be active all the time, as shown in the application freezes, the desktop restarts with a black screen, the phone freezes, and so on. As a result, Oems will be reluctant to produce these devices.

Evaluate application memory usage

So, how do we know how much memory our App uses?

Physical memory tracking

As mentioned above, the physical memory of the device is divided into many Pages. The Linux Kernel keeps track of the Pages used by each process, so just count the Pages used by the process:

But it’s more complicated than that, because some Pages are shared between processes:

Shared memory page counting method:

(1) Resident Set Size (RSS) : The App is fully responsible for:

(2) PSS (Proportional Set Size) : App is responsible for half of it, for example, as shown in the following figure, two processes are shared. If three processes are shared, one third is responsible:

(3) USS (Unique Set Size) :

In reality, however, you need at least system-level context to know and recognize RSS versus USS. Therefore, PSS is usually used to calculate, which also avoids overcounting or undercounting Shared Pages. You can use:

adb shell dumpsys meminfo -s [process] 
Copy the code

Command to view the PSS usage of a process:

TOTAL at the bottom represents the TOTAL memory used by the application in proportion.

Application memory usage analysis

If you want your app to support more features and have a cool UI, you need to allocate more memory. There is no such thing as wanting a horse to run and not eat grass:

Factors affecting memory usage:

(1) Application usage scenario: It is easy to understand, which page is more dazzling, dynamic and efficient, or uses webView, then the memory occupied by the App will be high:

(2) Platform configuration: It is easy to understand, for example, the higher the resolution of the phone is, the larger the memory occupied by the pictures of the same DP will be, so the memory occupied by the App on the high-end phone will definitely be higher than that of the low-end phone:

(3) Device memory pressure: The tighter the device memory is, the more likely GC will be triggered, resulting in lower memory occupied by App than when the device has sufficient memory:

So, you should evaluate your App’s memory footprint under the same memory pressure:

Since memory stress is difficult to control, it is recommended to clean up all processes with one click before evaluation and then test again.

Reduce application memory usage

Using Android Studio’s Memory Profiler, you can see a lot of information about what objects are currently allocated on the Java heap, their size, and their reference chains and referenced chains. Live Allocation has options such as Image heap, Zygote Heap, app heap, etc., but I recommend focusing only on app Heap. Since the Image heap and Zygote heap are inherited from the system at App startup, there is basically nothing we can do about this memory footprint:

I won’t go into too much detail about Memory profilers because tomorrow at 12:30 a.m. Esteban will explain the Profiler in detail. After all, it was developed by their team. So, I highly recommend that you attend tomorrow’s information session.

Analysis of memory usage beyond the Java Heap

As mentioned above, TOTAL is PSS, so what does this diagram mean besides Java Heap? What can we do about this footprint?

This is a bit more fun, because most of these sections are generated by the Android platform, and if you really want to understand them, you need to learn a lot of expertise. For example, how does the Framework implement View system and Resource management, how does Native Code implement, how does WebView work, how does Android Runtime execute your Code, How HAL manages your Graphics and Linux kernel virtual memory management etc.

By the way, I live here, in this orange square (Android Runtime) :

Diagnostics of memory usage generated by the Android platform

So, do we need tools to diagnose platform-generated footprint? First, we can use:

adb shell dumpsys meminfo -a [process]
Copy the code

To view more detailed information (the following data is the memory usage of the App developed by the author) :

Applications Memory Usage (in Kilobytes):
Uptime: 498024399 Realtime: 1230430304

** MEMINFO inpid 10898 [com.yuloran.wanandroid_java] ** Pss Pss Shared Private Shared Private SwapPss Heap Heap Heap Total Clean Dirty Dirty Clean Clean Dirty Size Alloc Free ------ ------ ------ ------ ------ ------ ------ ------ ------ ------ Native Heap 35822 0 824 35764 32 24 8740 75776 38786 36989 Dalvik Heap 4001 0 304 3552 72 412 240 6847 3424 3423 Dalvik Other 5256 0 48 5256 0 0 0 Stack 120 0 4 120 0 0 0 Ashmem 130 0 4 128 4 0 0 Gfx dev 2596 0 0 2596 0 0 0 Other dev 16 0 104 0 0 16 0 .so mmap 23782 22188 1132 504 13320 22188 15 .jar mmap 68 0 8 68 0 0 0 .apk mmap 8029 24 0 7684 1872 24 0 .ttf mmap 223 20 0 0 956 20 0 .dex mmap 21974 19864 0 20 13080 19864 0 .oat mmap 377 64 0 0 3620 64 0 .art mmap 6547 404  868 5852 7584 404 24 Other mmap 408 0 12 8 644 376 0 EGL mtrack 24660 0 0 24660 0 0 0 GL mtrack 4524 0 0 4524 0 0 0 Unknown 2130 0 184 2124 0 0 0 TOTAL 140702 42564 3492 92860 41184 43392 39 82623 42210 40412 Dalvik Details .Heap 3308 0  0 3308 0 0 0 .LOS 42 0 16 12 4 28 4 .LinearAlloc 4020 0 20 4020 0 0 0 .GC 384 0 16 384 0 0 0 .JITCache 596 0 0 596 0 0 0 .Zygote 583 0 288 164 68 384 0 .NonMoving 68 0 0 68 0 0 0 .IndirectRef 256 0 12 256 0 0 0 App Summary Pss(KB) ------ Java Heap: 9808 Native Heap: 35764 Code: 50436 Stack: 120 Graphics: 31780 Private Other: 8344 System: 4450 TOTAL: 140702 TOTAL SWAP PSS: 39 Objects Views: 207 ViewRootImpl: 1 AppContexts: 3 Activities: 1 Assets: 18 AssetManagers: 3 Local Binders: 24 Proxy Binders: 23 Parcel memory: 8 Parcel count: 34 Death Recipients: 3 OpenSSL Sockets: 0 WebViews: 0 SQL MEMORY_USED: 345 PAGECACHE_OVERFLOW: 55 MALLOC_SIZE: 117 DATABASES pgsz dbsz Lookaside(b) cache Dbname 4 20 41 17/38/5 /data/user/0/com.yuloran.wanandroid_java/databases/app_database.db 4 12 0/0/0 (attached) temp 4 20 40 3/19/4 /data/user/0/com.yuloran.wanandroid_java/databases/app_database.db (1)Copy the code
  • Private Dirty Memory is similar to Used Memory;
  • Private Clean Memory is similar to Cached Memory mentioned earlier.

Showmap, Ahat, debug malloc, etc. Because he goes on to say:

Basically: yes, but no need. Because it requires a lot of expertise, and a lot of data is visible but not controllable.

Memory Optimization Suggestions

(1) Optimize objects on the Java heap:

A lot of memory is not allocated in the Java heap, but its life cycle is tied to objects allocated on the Java heap:

So optimizing objects on the Java Heap can also help reclaim other types of memory.

(2) Reduce apK volume:

Because many files that take up disk space in APK also take up memory space at runtime:

Because apK occupies a fixed amount of disk space, it is easier to compress the APK size than to reduce the memory footprint. See Best Practices to Slim Down Your App Size for more APK Size optimization methods.

conclusion

This video is about the Low Memory Killer mechanism of Android, how to evaluate the Memory usage of your application, and how to reduce the Memory usage of your application. This video is based on the experience of Rechard Uhler, a developer at Google Android Runtime. You can say it’s very reliable.

In my own development experience, memory leaks are relatively easy to solve, but some leaks are caused by third-party SDKS or frameworks and can only be fixed by reflection. If the reflection can not be repaired, but there is no continuous leakage, that is, only once leakage, can also not be processed, or through business promotion to solve. Reducing memory footprint is more difficult, after all, to enrich the App, it is bound to take up more memory. Moreover, many projects are now developed by multi-person teams, where each person may only be responsible for a small piece of the application and lack of control over the entire application, making memory allocation even more difficult. Therefore, memory tuning requires extensive programming experience and architectural experience. In addition to Java, you also need to have a deep understanding of many Android UI controls, because on the Android platform, the majority of memory usage is always UI, mainly Bitmap.

Memory optimization has a long way to go.