低价智能手机 – Firefox OS 上的内存管理和优化

我们知道如何生成内存占用情况来调试内存泄漏并防止滥用内存资源。现在,我们想介绍一下在 Firefox OS 上有限的内存资源下的内存管理和优化。

如何在 Firefox OS 上获得更多内存?

当 Firefox OS 上内存不足时,有三种类型的事件可以获得更多内存

  1. 低内存杀手 (LMK)
  2. 内存压力事件
  3. 内存不足 (OOM)

低内存杀手 (LMK)

LMK 是一种通过杀死 Android 中的进程来获取内存资源的策略;我们将它整合到 Firefox OS 中。当缓存大小或可用内存低于指定阈值时,系统将触发 LMK 来杀死 oom_adj 大于 0 的进程并获取一些内存资源。

阈值在 b2g/app/b2g.js 中定义。让我们以以下表格为例

  • 当缓存大小和可用内存都低于 20MB 时,oom_adj 为 10 (BACKGROUND) 的进程将被 LMK 杀死
  • 当缓存大小和可用内存都低于 8MB 时,oom_adj 为 8 (BACKGROUND_HOMESCREEN) 的进程将被 LMK 杀死
  • 当缓存大小和可用内存都低于 4MB 时,名为 b2g 且 oom_adj 为 0 (MASTER) 的进程将被 LMK 杀死

顺便说一下,LMK 一次杀死一个进程,所以我们可以确定当 b2g 被 LMK 杀死时发生了内存泄漏。

// The kernel can only accept 6 (OomScoreAdjust, KillUnderKB) pairs. But it is
// okay, kernel will still kill processes with larger OomScoreAdjust first even
// its OomScoreAdjust don't have a corresponding KillUnderKB.

pref("hal.processPriorityManager.gonk.MASTER.OomScoreAdjust", 0);
pref("hal.processPriorityManager.gonk.MASTER.KillUnderKB", 4096);
pref("hal.processPriorityManager.gonk.MASTER.Nice", 0);

pref("hal.processPriorityManager.gonk.PREALLOC.OomScoreAdjust", 67);
pref("hal.processPriorityManager.gonk.PREALLOC.Nice", 18);

pref("hal.processPriorityManager.gonk.FOREGROUND_HIGH.OomScoreAdjust", 67);
pref("hal.processPriorityManager.gonk.FOREGROUND_HIGH.KillUnderKB", 5120);
pref("hal.processPriorityManager.gonk.FOREGROUND_HIGH.Nice", 0);

pref("hal.processPriorityManager.gonk.FOREGROUND.OomScoreAdjust", 134);
pref("hal.processPriorityManager.gonk.FOREGROUND.KillUnderKB", 6144);
pref("hal.processPriorityManager.gonk.FOREGROUND.Nice", 1);

pref("hal.processPriorityManager.gonk.FOREGROUND_KEYBOARD.OomScoreAdjust", 200);
pref("hal.processPriorityManager.gonk.FOREGROUND_KEYBOARD.Nice", 1);

pref("hal.processPriorityManager.gonk.BACKGROUND_PERCEIVABLE.OomScoreAdjust", 400);
pref("hal.processPriorityManager.gonk.BACKGROUND_PERCEIVABLE.KillUnderKB", 7168);
pref("hal.processPriorityManager.gonk.BACKGROUND_PERCEIVABLE.Nice", 7);

pref("hal.processPriorityManager.gonk.BACKGROUND_HOMESCREEN.OomScoreAdjust", 534);
pref("hal.processPriorityManager.gonk.BACKGROUND_HOMESCREEN.KillUnderKB", 8192);
pref("hal.processPriorityManager.gonk.BACKGROUND_HOMESCREEN.Nice", 18);

pref("hal.processPriorityManager.gonk.BACKGROUND.OomScoreAdjust", 667);
pref("hal.processPriorityManager.gonk.BACKGROUND.KillUnderKB", 20480);
pref("hal.processPriorityManager.gonk.BACKGROUND.Nice", 18);

内存压力事件

当内存压力事件被触发时,注册为内存压力事件观察者的服务将执行相应的操作,例如消除或最小化缓存(b2g 的临时数据)。内存压力事件的阈值在 b2g/app/b2g.js 中配置,默认值 (notifyLowMemUnderKB) 为 14MB。

// Fire a memory pressure event when the system has less than Xmb of memory
// remaining.  You should probably set this just above Y.KillUnderKB for
// the highest priority class Y that you want to make an effort to keep alive.
// (For example, we want BACKGROUND_PERCEIVABLE to stay alive.)  If you set
// this too high, then we'll send out a memory pressure event every Z seconds
// (see below), even while we have processes that we would happily kill in
// order to free up memory.
pref("hal.processPriorityManager.gonk.notifyLowMemUnderKB", 14336);

内存不足 (OOM)

Linux 内核提供了 OOM 策略。当可用内存低于指定阈值时,OOM 将通过内存使用量和 oom_score_adj 计算的评分来决定杀死哪个进程。整个操作将获得更多内存资源以满足系统的需求。触发 OOM 的可用内存的默认阈值 (min_free_kbytes) 为 1352KB。

如何使这三种内存策略在 Firefox OS 上平稳运行并达到优异的性能?

首先让我介绍一些可能出现的问题。如果这三种策略之间存在冲突,会发生什么?

  • 问题 1. 如果 LMK 阈值低于预期,系统将变得迟缓。使用较低的阈值,当内存不足时,系统无法杀死任何进程以获取更多内存。然后由于触发了 kswapd 并大量消耗 CPU,性能将会受到影响。如果阈值高于预期,用户会感到不便,因为应用程序很容易被杀死。
  • 问题 2. 如果内存压力事件的阈值过高,消除和最小化缓存的活动将过于激进。当内存充足时,相关的活动会浪费 CPU 资源,导致性能下降。如果阈值过低,系统将无法及时释放内存。
  • 问题 3. 当 OOM 比 LMK 更早触发时,会导致崩溃或致命错误。如果 OOM 首先被触发,B2g 或其他系统应用程序将在后台/前台应用程序之前被杀死。当内存不足时,系统应该杀死后台/前台应用程序,而不是 b2g 或系统应用程序。

针对上述问题,我们需要一组参数来解决它们

  1. 为了体验良好的性能,我们允许杀死后台应用程序以获取更多内存,这将使前台应用程序运行流畅。因此,我们增加了 BACKGROUND_HOMESCREEN.KillUnderKBBACKGROUND.KillUnderKB 的值,以便更容易地杀死主屏幕和后台应用程序。
  2. 我们应该在杀死可感知的后台应用程序之前触发内存压力事件。也就是说,最好将 notifyLowMemUnderKB 的值设置在 BACKGROUND_HOMESCREEN.KillUnderKBBACKGROUND_PERCEIVABLE.KillUnderKB 之间。
  3. 为了解决问题 3,LMK 必须在 OOM 开始工作之前启动,这意味着 min_free_kbytes 的值应该低于 MASTER.KillUnderKB 的值。

让我们以 Tarako (sc6821, 128MB 内存设备) 为例。由 adb shell b2g-info 导出的内存设置如下

Low-memory killer parameters:

  notify_trigger 14336 KB

  oom_adj min_free
        0  4096 KB
        1  5120 KB
        2  6144 KB
        6  7168 KB
        8 16384 KB
       10 18432 KB

快速调整内存的技巧

除了修改 b2g/app/b2g.js 中的参数外,Firefox OS 还提供了一种有效的方法来验证你修改的内存设置。开发人员可以通过修改设备上的 /system/b2g/defaults/pref/dev-pref.js 来添加他们自己的修改设置。此文件中的参数将覆盖 b2g/app/b2g.js 中的默认值。

然而,还有很多方法可以优化低价智能手机的内存。我们在 bugzilla 上进行了更详细的技术讨论。加入 Firefox OS 开发,一起改变世界吧!

关于 Robert Nyman [荣誉编辑]

Mozilla Hacks 的技术布道者和编辑。进行演讲和博客,主题包括 HTML5、JavaScript 和开放网络。Robert 是 HTML5 和开放网络的坚定支持者,自 1999 年起就从事网络前端开发工作——在瑞典和纽约市。他还定期在 http://robertnyman.com 上发表博客,喜欢旅行和结识新朋友。

更多来自 Robert Nyman [荣誉编辑] 的文章...


2 条评论

  1. jbruggem

    有趣的是,你正在研究这个问题。在我的运行着 FxOS 2.1 的手机上,我发现很多泄漏。手机使用超过 24 小时后,它变得非常慢(动画卡顿,打字变得非常痛苦),即使我关闭所有打开的应用程序/窗口。

    2014 年 9 月 22 日 下午 1:57

  2. Luke

    这听起来像是实现细节——你能解释一下“我们知道如何生成内存占用情况来调试内存泄漏并防止滥用内存资源”吗?如何使用 Firefox Devtools 调试移动或桌面上的内存泄漏?

    2014 年 9 月 22 日 下午 7:50

本文的评论已关闭。