

















在Android 中的卡顿丢帧原因概述 - 系统篇 这篇文章中, 实际案例这里我们有列举一些由于系统低内存导致的卡顿, 由于 Android 低内存对整机性能影响比较大, 所以单独写一篇文章, 来概述系统低内存对整机性能的影响 .
随着 Android 系统版本的更迭, 以及 App 的代码膨胀, Android 系统对内存的需求越来越大, 但是目前市面上还存在着大量的 4G 内存以下的机器, 这部分用户就很容易遇到整机低内存的情况, 尤其是在系统大版本更新和 App 越装越多的情况下 .
Android 低内存会导致性能问题, 具体表现就是响应慢和卡顿 . 比如启动一个应用要花比平时更长的时间 ; 滑动列表会掉更多帧 ; 后台的进程减少导致冷启动变多 ; 手机很容易发热发烫等, 下面我会概述发生这些性能问题的原因 . Debug 的方法, 以及可能的优化措施 .
1 | [kswapd0] lowmemorykiller: Killing 'u.mzsyncservice' (15609) (tgid 15609), adj 906, |
上面这段 Log 的意思是说, 由于 mem 低于我们设定的 900 的水位线 (261272kB),所以把 pid 为 15609 的 mzsyncservice 这个进程杀掉(这个进程的 adj 是 906 )
1 | MemTotal: 5630104 kB |
Linux 系统的 page cache 链表中有时会出现一些还没准备好的 page ( 即还没把磁盘中的内容完全地读出来 ), 而正好此时用户在访问这个 page 时就会出现 wait_on_page_locked_killable 阻塞了. 只有系统当 io 操作很繁忙时, 每笔的 io 操作都需要等待排队时, 极其容易出现且阻塞的时间往往会比较长.
当出现大量的 IO 操作的时候,应用主线程的 Uninterruptible Sleep 也会变多,此时涉及到 io 操作(比如 view ,读文件,读配置文件、读 odex 文件),都会触发 Uninterruptible Sleep , 导致整个操作的时间变长
低内存会触发 Low Memory Killer 进程频繁进行扫描和杀进程,kswapd0 是一个内核工作线程,内存不足时会被唤醒,做内存回收的工作。 当内存频繁在低水位的时候,kswapd0 会被频繁唤醒,占用 cpu ,造成卡顿和耗电。
比如下面这个情况, kswapd0 占用了 855 的超大核 cpu7 ,而且是满频在跑,耗电可想而知,如果此时前台应用的主线程跑到了 cpu7 上,很大可能会出现 cpu 竞争,导致调度不到而丢帧。
对 AMS 的影响主要集中在进程的查杀上面, 由于 LMK 的介入, 处于 Cache 状态的进程很容易被杀掉, 然后又被他们的父进程或者其他的应用所拉起来, 导致陷入了一种死循环 . 对系统 CPU \ Memory \ IO 等资源的影响非常大.
比如下面就是一次 Monkey 之后的结果, QQ 在短时间内频繁被杀和重启 .
1 | 07-23 14:32:16.932 1435 1510 I am_proc_start: [0,30387,10145,com.tencent.mobileqq,restart,com.tencent.mobileqq] |
手机经过长时间老化使用整机卡顿一下, 或者整体比刚刚开机的时候操作要慢, 可能是因为触发了内存回收或者 block io, 而这两者又经常有关联 . 内存回收可能触发了 fast path 回收 \ kswapd 回收 \ direct reclaim 回收 \ LMK杀进程回收等。(fast path 回收不进行回写)
回收的内容是匿名页 swapout 或者 file-backed 页写回和清空。(假设手机都是 swap file 都是内存,不是 disk), 涉及到 file 的,都可能操作 io,增加 block io 的概率。
还有更常见的是打开之前打开过的应用,没有第一次打开的快,需要加载或者卡一段时间 . 可能发生了 do_page_fault,这条路径经常见到 block io 在 wait_on_page_bit_killable(),如果是 swapout 内存,就要 swapin 了。如果是普通文件,就要 read out in pagecache/disk.
do_page_fault —> lock_page_or_retry -> wait_on_page_bit_killable 里面会判断 page 是否置位 PG_locked, 如果置位就一直阻塞, 直到 PG_locked 被清除 , 而 PG_locked 标志位是在回写开始时和 I/O 读完成时才会被清除,而 readahead 到 pagecache 功能也对 block io 产生影响,太大了增加阻塞概率。
下面这个 Trace 是低内存情况下 , 抓取的一个 App 的冷启动 , 我们只取应用启动到第一帧显示的部分 ,总耗时为2s 。
可以看到其 Running 的总时间是 682 ms ,
低内存情况下, 这个 App 从 bindApplication 到第一帧显示, 共花费了 2s . 从下面的 Thread 信息那里可以看到
从这段时间内的 CPU 使用情况来看, 除了 HeapTaskDeamon 跑的比较多之外, 其他的内存和 io 相关的进程也非常多, 比如若干个 kworker 和 kswapd0.
正常内存情况下, 这个 App 从 bindApplication 到第一帧显示, 只需要 1.22s . 从下面的 Thread 信息那里可以看到
从这段时间内的 CPU 使用情况来看, 除了 HeapTaskDeamon 跑的比较多之外, 其他的内存和 io 相关的进程非常少.
下面列举的只是一些经验之谈, 具体问题还是得具体分析, 在 Android 平台上, 对三方应用的管控是非常重要的, 很多小白用户, 一大堆常驻通知和后台服务, 导致这些 App 的优先级非常高, 很难被杀掉 . 导致整机的内存长时间比较低 . 所以做系统的必要的优化之后, 就要着重考虑对三方应用的查杀和管控逻辑, 尽量减少后台进程的个数, 在必要的时候, 清理掉无用的进程来释放内存个前台应用使用.
此内容由惯性聚合(RSS阅读器)自动聚合整理,仅供阅读参考。 原文来自 — 版权归原作者所有。