Android Performance

Android Jank and Frame Drops Due to Low Memory

Word count: 2.9kReading time: 16 min
2019/09/18
loading

In the article Android Jank and Frame Drops Due to System Issues, we listed some actual cases of jank caused by system low memory. Since low memory significantly impacts overall device performance, I’m writing a separate article to outline how system low memory affects device performance.

As Android system versions evolve and apps’ codebases expand, Android’s memory requirements continue to grow. However, many devices in the market still have less than 4GB of RAM. These users easily encounter system-wide low memory situations, especially after major system updates or as more apps are installed.

Android low memory leads to performance issues, specifically manifested as slow response and jank. For example: launching an app takes longer than usual; scrolling lists drops more frames; reduced background processes increase cold starts; phones easily overheat. Below, I’ll outline the reasons for these performance issues, debugging methods, and potential optimization measures.

0. Android Jank and Frame Drops: Methodology
1. Android Jank and Frame Drops Due to System Issues
2. Android Jank and Frame Drops Due to App Issues
3. Android Jank and Frame Drops Due to Low Memory

Data and Behavioral Characteristics of Low Memory

Meminfo Information

The simplest method is using Android’s built-in Dumpsys meminfo tool:

1
2
3
4
5
6
7
8
adb shell dumpsys meminfo
......
Total RAM: 7,658,060K (status moderate)
Free RAM: 550,200K ( 78,760K cached pss + 156,ba480K cached kernel + 314,960K free)
Used RAM: 7,718,091K (6,118,703K used pss + 1,599,388K kernel)
Lost RAM: -319,863K
ZRAM: 2,608K physical used for 301,256K in swap (4,247,544K total swap)
Tuning: 256 (large 512), oom 322,560K, restore limit 107,520K (high-end-gfx)

If the system is in low memory, it exhibits these characteristics:

  1. FreeRam value is very small, Used RAM value is very large
  2. ZRAM usage is very high (if ZRAM is enabled)

LMK && kswapd Thread Activity

During low memory, LMK becomes very active. You can see LMK killing processes in Kernel Log:

1
2
3
4
5
6
7
8
9
[kswapd0] lowmemorykiller: Killing 'u.mzsyncservice' (15609) (tgid 15609), adj 906,
to free 28864kB on behalf of 'kswapd0' (91) because
cache 258652kB is below limit 261272kB for oom score 906
Free memory is -5540kB above reserved.
Free CMA is 3172kB
Total reserve is 227288kB
Total free pages is 271748kB
Total file cache is 345384kB
GFP mask is 0x14000c0

This log means: because memory is below our set 900 watermark (261272kB), it kills the process with PID 15609 (mzsyncservice, with adj 906).

proc/meminfo

This is where Linux Kernel displays meminfo. For meminfo interpretation, refer to this article: /PROC/MEMINFO之谜

From the results, when the system is in low memory, both MemFree and MemAvailable values are small.

shell cat proc/meminfo
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
MemTotal:        5630104 kB
MemFree: 148928 kB
MemAvailable: 864172 kB
Buffers: 28464 kB
Cached: 1003144 kB
SwapCached: 19844 kB
Active: 1607512 kB
Inactive: 969208 kB
Active(anon): 1187828 kB
Inactive(anon): 426192 kB
Active(file): 419684 kB
Inactive(file): 543016 kB
Unevictable: 62152 kB
Mlocked: 62152 kB
SwapTotal: 2097148 kB
SwapFree: 42576 kB
Dirty: 3604 kB
Writeback: 0 kB
AnonPages: 1602928 kB
Mapped: 996768 kB
Shmem: 7284 kB
Slab: 306440 kB
SReclaimable: 72320 kB
SUnreclaim: 234120 kB
KernelStack: 89776 kB
PageTables: 107572 kB
NFS_Unstable: 0 kB
Bounce: 0 kB
WritebackTmp: 0 kB
CommitLimit: 4912200 kB
Committed_AS: 118487976 kB
VmallocTotal: 263061440 kB
VmallocUsed: 0 kB
VmallocChunk: 0 kB
CmaTotal: 303104 kB
CmaFree: 3924 kB

System-wide Lag & Slow Response

During low memory, the entire device feels much more sluggish than during normal memory conditions. Tapping apps or launching applications feels unresponsive or slow.

Specific Impacts of Low Memory on Performance

Impact on Main Thread IO Operations

The main thread experiences大量IO相关的问题 (numerous IO-related issues):

  1. Reflected in Trace as大量黄色Trace State出现 (numerous yellow Trace States appear), e.g.: Uninterruptible Sleep | WakeKill - Block I/O.
  2. Check its Block information (kernel callsite when blocked:: "wait_on_page_bit_killable+0x78/0x88)

Linux system’s page cache链表 (linked list) sometimes contains pages that aren’t fully prepared (i.e., haven’t completely read content from disk). When users恰好访问这个page (恰好访问这个page) at that moment, wait_on_page_locked_killable阻塞 (blocks). This极易出现 (easily occurs) when system IO operations are very繁忙 (busy), with each IO operation needing to wait in queue, and阻塞时间往往比较长 (blocking time tends to be long).

当出现大量的IO操作的时候 (When大量IO操作 occur), the application main thread’s Uninterruptible Sleep also increases. At this point, any IO operation (e.g., view, reading files, reading configuration files, reading odex files) triggers Uninterruptible Sleep,导致整个操作的时间变长 (causing the entire operation time to lengthen).

CPU Competition

Low memory triggers Low Memory Killer processes to频繁进行扫描和杀进程 (frequently scan and kill processes). kswapd0 is a kernel worker thread awakened during memory不足 (insufficiency) to perform memory reclamation. When memory频繁在低水位 (frequently at low watermarks), kswapd0 gets频繁唤醒 (frequently awakened),占用cpu (occupying CPU), causing jank and battery drain.

For example, in the following situation, kswapd0 occupies the超大核 (big core) CPU7 at满频 (full frequency), consuming significant power. If the foreground app’s main thread runs on CPU7 at this time, CPU competition很可能出现 (is very likely to occur),导致调度不到而丢帧 (causing missed scheduling and frame drops).

HeapTaskDaemon also typically runs at high frequency during low memory.

, to perform memory-related operations.

Process Frequent Killing and Restarting

The impact on AMS mainly集中在进程的查杀上面 (focuses on process killing). Due to LMK intervention, processes in Cache state很容易被杀掉 (are easily killed), then又被他们的父进程或者其他的应用所拉起来 (then restarted by their parent processes or other applications),导致陷入了一种死循环 (leading to a death loop). This significantly impacts system CPU, Memory, IO, and other resources.

For example, below is the result after a Monkey test—QQ gets频繁被杀和重启 (frequently killed and restarted) in a short time.

14:32:16.932 1435 1510 I am_proc_start: [0,30387,10145,com.tencent.mobileqq,restart,com.tencent.mobileqq]
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
07-23 14:32:16.969  1435  3420 I am_proc_bound: [0,30387,com.tencent.mobileqq]
07-23 14:32:16.979 1435 3420 I am_kill : [0,30387,com.tencent.mobileqq,901,empty #3]
07-23 14:32:16.996 1435 3420 I am_proc_died: [0,30387,com.tencent.mobileqq,901,18]
07-23 14:32:17.028 1435 1510 I am_proc_start: [0,30400,10145,com.tencent.mobileqq,restart,com.tencent.mobileqq]
07-23 14:32:17.054 1435 3420 I am_proc_bound: [0,30400,com.tencent.mobileqq]
07-23 14:32:17.064 1435 3420 I am_kill : [0,30400,com.tencent.mobileqq,901,empty #3]
07-23 14:32:17.082 1435 3420 I am_proc_died: [0,30400,com.tencent.mobileqq,901,18]
07-23 14:32:17.114 1435 1510 I am_proc_start: [0,30413,10145,com.tencent.mobileqq,restart,com.tencent.mobileqq]
07-23 14:32:17.139 1435 3420 I am_proc_bound: [0,30413,com.tencent.mobileqq]
07-23 14:32:17.149 1435 3420 I am_kill : [0,30413,com.tencent.mobileqq,901,empty #3]
07-23 14:32:17.166 1435 3420 I am_proc_died: [0,30413,com.tencent.mobileqq,901,18]
07-23 14:32:17.202 1435 1510 I am_proc_start: [0,30427,10145,com.tencent.mobileqq,restart,com.tencent.mobileqq]
07-23 14:32:17.216 1435 3420 I am_proc_bound: [0,30427,com.tencent.mobileqq]
07-23 14:32:17.226 1435 3420 I am_kill : [0,30427,com.tencent.mobileqq,901,empty #3]
07-23 14:32:17.249 1435 3420 I am_proc_died: [0,30427,com.tencent.mobileqq,901,18]
07-23 14:32:17.278 1435 1510 I am_proc_start: [0,30440,10145,com.tencent.mobileqq,restart,com.tencent.mobileqq]
07-23 14:32:17.299 1435 3420 I am_proc_bound: [0,30440,com.tencent.mobileqq]
07-23 14:32:17.309 1435 3420 I am_kill : [0,30440,com.tencent.mobileqq,901,empty #3]
07-23 14:32:17.329 1435 2116 I am_proc_died: [0,30440,com.tencent.mobileqq,901,18]
07-23 14:32:17.362 1435 1510 I am_proc_start: [0,30453,10145,com.tencent.mobileqq,restart,com.tencent.mobileqq]
07-23 14:32:17.387 1435 2116 I am_proc_bound: [0,30453,com.tencent.mobileqq]
07-23 14:32:17.398 1435 2116 I am_kill : [0,30453,com.tencent.mobileqq,901,empty #3]
07-23 14:32:17.420 1435 2116 I am_proc_died: [0,30453,com.tencent.mobileqq,901,18]
07-23 14:32:17.447 1435 1510 I am_proc_start: [0,30466,10145,com.tencent.mobileqq,restart,com.tencent.mobileqq]
07-23 14:32:17.474 1435 2116 I am_proc_bound: [0,30466,com.tencent.mobileqq]
07-23 14:32:17.484 1435 2116 I am_kill : [0,30466,com.tencent.mobileqq,901,empty #3]
07-23 14:32:17.507 1435 2116 I am_proc_died: [0,30466,com.tencent.mobileqq,901,18]
07-23 14:32:17.533 1435 1510 I am_proc_start: [0,30479,10145,com.tencent.mobileqq,restart,com.tencent.mobileqq]
07-23 14:32:17.556 1435 2116 I am_proc_bound: [0,30479,com.tencent.mobileqq]
07-23 14:32:17.566 1435 2116 I am_kill : [0,30479,com.tencent.mobileqq,901,empty #3]
07-23 14:32:17.587 1435 2116 I am_proc_died: [0,30479,com.tencent.mobileqq,901,18]
07-23 14:32:17.613 1435 1510 I am_proc_start: [0,30492,10145,com.tencent.mobileqq,restart,com.tencent.mobileqq]
07-23 14:32:17.636 1435 2116 I am_proc_bound: [0,30492,com.tencent.mobileqq]
07-23 14:32:17.646 1435 2116 I am_kill : [0,30492,com.tencent.mobileqq,901,empty #3]
07-23 14:32:17.667 1435 2116 I am_proc_died: [0,30492,com.tencent.mobileqq,901,18]

In the corresponding Systrace - SystemServer, you can see AM频繁杀QQ和起QQ (frequently killing and starting QQ):

The Kernel portion of this Trace also shows busy CPU:

Impact on Memory Allocation and Triggering IO

After prolonged aging use, the phone experiences整机卡顿一下 (system-wide jank) or整体比刚刚开机的时候操作要慢 (overall slower operation than right after boot). This may be due to triggered memory reclamation or block IO, which are often related. Memory reclamation may trigger fast path reclamation, kswapd reclamation, direct reclaim reclamation, LMK process killing reclamation, etc. (fast path reclamation doesn’t perform writeback).

Reclaimed content is anonymous page swapout or file-backed page writeback and clearing. (Assuming手机都是swap file都是内存 (phones use swap files in memory, not disk)). Anything involving files may trigger IO operations, increasing block IO probability.

More commonly: opening previously opened applications isn’t as fast as the first time—needing to load or卡一段时间 (stutter for a while). This may have triggered do_page_fault. This path frequently shows block IO in wait_on_page_bit_killable(). If it’s swapout memory, it needs swapin. If it’s a regular file, it needs read out in pagecache/disk.

do_page_faultlock_page_or_retrywait_on_page_bit_killable checks if the page has the PG_locked bit set. If set, it blocks until PG_locked is cleared. The PG_locked flag is cleared only at writeback start and I/O read completion. The readahead to pagecache functionality also affects block IO—too large increases blocking probability.

Examples

The following Trace is a冷启动 (cold start) of an app抓取 (captured) under low memory conditions. We only take the portion from app launch to first frame display—total耗时 (duration) 2s.
You can see its Running total time is 682 ms.

Startup Under Low Memory

Under low memory, this app from bindApplication to first frame display花费了 (took) 2s. From the Thread information below:

  1. Uninterruptible Sleep | WakeKill - Block I/O and Uninterruptible Sleep这两栏总共花费 (these two columns总共花费) ~750 ms (对比下面正常情况才130 ms (compared to only 130 ms under normal conditions below))
  2. Running time is ~600 ms (对比下面正常情况才624 ms (compared to 624 ms under normal conditions below),相差不大 (not much difference))

From CPU usage during this period, besides HeapTaskDaemon running较多 (more), other memory and IO-related processes are also非常多 (very numerous), like several kworker and kswapd0.

Startup Under Normal Memory

Under normal memory, this app from bindApplication to first frame display只需要 (only needs) 1.22s. From the Thread information below:

  1. Uninterruptible Sleep | WakeKill - Block I/O and Uninterruptible Sleep这两栏总共才 (these two columns总共才) 130 ms.
  2. Running time is 624 ms.

From CPU usage during this period, besides HeapTaskDaemon running较多 (more), other memory and IO-related processes are非常少 (very few).

Possible Optimization Solutions (from practical experience and expert sharing)

The following列举的只是一些经验之谈 (listed are just一些经验之谈).具体问题还是得具体分析 (specific problems require specific analysis). On the Android platform,三方应用的管控 (third-party app management) is非常重要 (very important). Many小白用户 (novice users) have一大堆常驻通知和后台服务 (a bunch of persistent notifications and background services),导致这些App的优先级非常高 (causing these apps to have very high priority),很难被杀掉 (hard to kill).导致整机的内存长时间比较低 (causing device memory to remain low for long periods). So after必要的系统优化 (necessary system optimization), focus on三方应用的查杀和管控逻辑 (third-party app killing and management logic),尽量减少后台进程的个数 (minimize background process count),在必要的时候 (when necessary),清理掉无用的进程来释放内存个前台应用使用 (clean up useless processes to release memory for foreground apps).

  1. Increase extra_free_kbytes value
  2. Increase disk I/O read/write rates, e.g., use UFS 3.0, solid-state drives
  3. Avoid setting too large read_ahead_kb values
  4. Use cgroup’s blkio to limit background process IO read operations, shortening foreground IO response time
  5. Perform memory reclamation提前 (in advance), avoiding users encountering it during app usage and perceiving slight jank
  6. Increase LMK efficiency, avoiding无效的kill (ineffective kills)
  7. kswapd周期性回收更多的high水位 (periodically reclaim more high watermarks)
  8. Adjust swappiness to balance pagecache and swap
  9. Strategy: Implement特殊策略 (special strategies) for low-memory devices, e.g., more激进杀进程 (aggressive process killing) (this降低用户体验 (reduces user experience), so balance performance and user experience)
  10. Strategy: When内存不足 (memory insufficient), remind users (or not) to kill unnecessary background processes
  11. Strategy: When内存严重不足且无法恢复 (memory severely insufficient and unrecoverable), prompt users to restart phone

Optimizing for low memory requires balancing system control and user experience. Managing third-party apps—many of which use aggressive keep-alive and background services—is crucial.

  1. Tune extra_free_kbytes: Increase the threshold for memory reclamation.
  2. Increase Disk I/O Speeds: Use UFS 3.0 or better storage hardware.
  3. Optimize read_ahead_kb: Avoid excessively high values that increase block probability.
  4. Cgroup blkio: Use cgroups to limit background I/O, prioritizing foreground responsiveness.
  5. Proactive Reclamation: Reclaim memory before the user interacts with the app.
  6. LMK Efficiency: Ensure LMK kills the most effective targets to avoid “death-restart” loops.
  7. Refine swappiness: Balance anonymous page swapping vs. file cache clearing.
  8. OEM Policies: Implement more aggressive process management on low-RAM devices.
  9. User Education: Suggest restarting the phone or clearing background apps when memory is critically low.

References

  1. https://blog.csdn.net/qkhhyga2016/article/details/79540119
  2. https://blog.csdn.net/zsj100213/article/details/82427527

Zhihu Version of This Article

Since blog comments aren’t convenient for discussion, you can visit the Zhihu version of this article for likes and交流:

知乎 - Android 中的卡顿丢帧原因概述 - 低内存篇

About Me && Blog

Below is my personal introduction and related links. I look forward to exchanging ideas with fellow professionals. “When three people walk together, one must be my teacher!”

  1. Blogger Introduction : Contains personal WeChat and WeChat group links.
  2. Blog Content Navigation : A navigation guide for my blog content.
  3. Curated Excellent Blog Articles - Android Performance Optimization Must-Knows : Welcome self-recommendations and recommendations (WeChat private chat is fine).
  4. Android Performance Optimization Knowledge Planet : Welcome to join, thanks for your support!

One walks faster alone, but a group walks further together.

Scan WeChat QR Code

CATALOG
  1. 1. Data and Behavioral Characteristics of Low Memory
    1. 1.1. Meminfo Information
    2. 1.2. LMK && kswapd Thread Activity
    3. 1.3. proc/meminfo
    4. 1.4. System-wide Lag & Slow Response
  2. 2. Specific Impacts of Low Memory on Performance
    1. 2.1. Impact on Main Thread IO Operations
    2. 2.2. CPU Competition
    3. 2.3. Process Frequent Killing and Restarting
    4. 2.4. Impact on Memory Allocation and Triggering IO
  3. 3. Examples
    1. 3.1. Startup Under Low Memory
    2. 3.2. Startup Under Normal Memory
  4. 4. Possible Optimization Solutions (from practical experience and expert sharing)
  5. 5. References
  6. 6. Zhihu Version of This Article
  7. 7. About Me && Blog