内存使用量

此文件包含有关 Zircon 中的内存管理和诊断的信息,并讨论了检查进程和系统内存使用情况的方法。

进程可以通过 3 种方式使用内存:

  1. 以堆、线程堆栈、可执行代码 + 数据的形式映射内存。 此内存由 VMAR 表示,VMAR 反过来又保存对 VMO 的引用。程序员通常通过内存地址与此内存连接。
  2. 独立的 VMO。这些是不通过 VMAR 映射的内存页集。程序员通过句柄(通常是发出 vmo_readvmo_write)与此内存连接。
  3. 内核对象句柄形式的内核内存。

Fuchsia 遵循过度使用模型:进程可以分配的内存量超出给定时刻能够满足的需求,并且在写入内存页面时,内核会动态地进行物理分配(有线)。

用户空间内存

哪些进程占用了全部内存?

转储进程总内存用量

使用 ps 工具:

$ ps
TASK           PSS PRIVATE  SHARED NAME
j:1028       32.9M   32.8M         root
  p:1043   1386.3k   1384k     28k bin/devmgr
  j:1082     30.0M   30.0M         zircon-drivers
    p:1209  774.3k    772k     28k /boot/bin/acpisvc
    p:1565  250.3k    248k     28k driver_host
    p:1619  654.3k    652k     28k driver_host
    p:1688  258.3k    256k     28k driver_host
    p:1867 3878.3k   3876k     28k driver_host
    p:1916   24.4M   24.4M     28k driver_host
  j:1103   1475.7k   1464k         zircon-services
    p:1104  298.3k    296k     28k crashlogger
    p:1290  242.3k    240k     28k netsvc
    p:2115  362.3k    360k     28k sh:console
    p:2334  266.3k    264k     28k sh:vc
    p:2441  306.3k    304k     28k /boot/bin/ps
TASK           PSS PRIVATE  SHARED NAME

PSS(比例共享状态)是一个字节数,用于估算进程消耗的进程中映射物理内存量。其值为 PRIVATE + (SHARED / sharing-ratio),其中 sharing-ratio 取决于共享此进程中每个页面的进程数量。

这样做的目的是,例如,如果四个进程共享一个页面,则该页面上有 1/4 的字节将分别包含在这四个进程的 PSS 中。如果两个进程共享不同的页面,则每个进程都会获得该页面的 1/2 字节。

PRIVATE 是仅由此进程映射的字节数。也就是说,没有其他进程映射此内存。请注意,这并未考虑未映射的专用 VMO。

SHARED 是此进程和至少一个其他进程映射的字节数。请注意,这未考虑未映射的共享 VMO。它也没有指明共享内存的进程数量:可能为 2,也可能为 50。

直观呈现内存用量

如果您有 Fuchsia build,则可以使用树状图来直观呈现系统的内存用量。

  1. 在主机上,从 Fuchsia 检出的根目录运行以下命令:

    ./scripts/fx shell memgraph -vt | ./scripts/memory/treemap.py > mem.html

  2. 在浏览器中打开 mem.html

memgraph 工具会生成系统任务和内存信息的 JSON 说明,然后由 treemap.py 脚本解析。-vt 表示在输出中包含 VMO 和线程。

转储进程的详细内存映射

如果您想了解特定进程为何使用如此多的内存,可以对其 koid 运行 vmaps 工具(koid 是运行 ps 时显示的 ID),以查看它映射到内存中的内容。

$ vmaps help
Usage: vmaps <process-koid>

Dumps a process's memory maps to stdout.

First column:
  "/A" -- Process address space
  "/R" -- Root VMAR
  "R"  -- VMAR (R for Region)
  "M"  -- Mapping

  Indentation indicates parent/child relationship.

列标记:

  • :sz:条目的虚拟大小(以字节为单位)。并非所有页面都必须由物理内存提供支持。
  • :res:条目中“常驻”的内存量(以字节为单位);即支持该条目的物理内存量。此内存可能是私有的(只能由此进程访问),也可能由多个进程共享。
  • :vmo:映射到此区域的 VMO 的 koid
$ vmaps 2470
/A ________01000000-00007ffffffff000    128.0T:sz                    'proc:2470'
/R ________01000000-00007ffffffff000    128.0T:sz                    'root'
...
# This 'R' region is a dynamic library. The r-x section is .text, the r--
# section is .rodata, and the rw- section is .data + .bss.
R  00000187bc867000-00000187bc881000      104k:sz                    'useralloc'
 M 00000187bc867000-00000187bc87d000 r-x   88k:sz   0B:res  2535:vmo 'libfdio.so'
 M 00000187bc87e000-00000187bc87f000 r--    4k:sz   4k:res  2537:vmo 'libfdio.so'
 M 00000187bc87f000-00000187bc881000 rw-    8k:sz   8k:res  2537:vmo 'libfdio.so'
...
# This 2MB anonymous mapping is probably part of the heap.
M  0000246812b91000-0000246812d91000 rw-    2M:sz  76k:res  2542:vmo 'mmap-anonymous'
...
# This region looks like a stack: a big chunk of virtual space (:sz) with a
# slightly-smaller mapping inside (accounting for a 4k guard page), and only a
# small amount actually committed (:res).
R  0000358923d92000-0000358923dd3000      260k:sz                    'useralloc'
 M 0000358923d93000-0000358923dd3000 rw-  256k:sz  16k:res  2538:vmo ''
...
# The stack for the initial thread, which is allocated differently.
M  0000400cbba84000-0000400cbbac4000 rw-  256k:sz   4k:res  2513:vmo 'initial-stack'
...
# The vDSO, which only has .text and .rodata.
R  000047e1ab874000-000047e1ab87b000       28k:sz                    'useralloc'
 M 000047e1ab874000-000047e1ab87a000 r--   24k:sz  24k:res  1031:vmo 'vdso/stable'
 M 000047e1ab87a000-000047e1ab87b000 r-x    4k:sz   4k:res  1031:vmo 'vdso/stable'
...
# The main binary for this process.
R  000059f5c7068000-000059f5c708d000      148k:sz                    'useralloc'
 M 000059f5c7068000-000059f5c7088000 r-x  128k:sz   0B:res  2476:vmo '/boot/bin/sh'
 M 000059f5c7089000-000059f5c708b000 r--    8k:sz   8k:res  2517:vmo '/boot/bin/sh'
 M 000059f5c708b000-000059f5c708d000 rw-    8k:sz   8k:res  2517:vmo '/boot/bin/sh'
...

您还可以使用 zxdb 中的 aspace 命令显示内存映射。

转储与进程关联的所有 VMO

vmos <pid>

这还会显示未映射的 VMO(psvmaps 当前均不考虑这些 VMO)。

此图还会显示给定 VMO 是否是子级,以及其父级的 koid。

$ vmos 1118
rights  koid parent #chld #map #shr    size   alloc name
rwxmdt  1170      -     0    1    1      4k      4k stack: msg of 0x5a
r-xmdt  1031      -     2   28   14     28k     28k vdso/stable
     -  1298      -     0    1    1      2M     68k jemalloc-heap
     -  1381      -     0    3    1    516k      8k self-dump-thread:0x12afe79c8b38
     -  1233   1232     1    1    1   33.6k      4k libbacktrace.so
     -  1237   1233     0    1    1      4k      4k data:libbacktrace.so
...
     -  1153   1146     1    1    1  883.2k     12k ld.so.1
     -  1158   1153     0    1    1     16k     12k data:ld.so.1
     -  1159      -     0    1    1     12k     12k bss:ld.so.1
rights  koid parent #chld #map #shr    size   alloc name

列数:

  • rights:如果进程通过句柄指向 VMO,则此列将显示该句柄具有的零或多项权限:
    • rZX_RIGHT_READ
    • wZX_RIGHT_WRITE
    • xZX_RIGHT_EXECUTE
    • mZX_RIGHT_MAP
    • dZX_RIGHT_DUPLICATE
    • tZX_RIGHT_TRANSFER
    • 注意:非标识名条目在此列中只有一个“-”。
  • koid:VMO 的 koid(如果有)。否则为 0。没有 Koid 的 VMO 是由内核创建的,从未具有用户空间句柄。
  • parent:VMO 的父级的子级(如果是子级)。
  • #chld:VMO 的活跃子级数量。
  • #map:VMO 当前映射到 VMAR 的次数。
  • #shr:映射(共享)VMO 的进程数量。
  • size:VMO 的当前大小(以字节为单位)。
  • alloc:分配给 VMO 的物理内存量,以字节为单位。
    • 注意:如果此列包含值 phys,则表示 VMO 指向原始物理地址范围,如内存映射设备。phys 个 VMO 不会消耗 RAM。
  • name:VMO 的名称,如果名称为空,则为 -

为了将其与 ps 相关联:每个 VMO 对其映射部分有所贡献(因为并非所有或 VMO 的任何页面都会被映射):

PRIVATE =  #shr == 1 ? alloc : 0
SHARED  =  #shr  > 1 ? alloc : 0
PSS     =  PRIVATE + (SHARED / #shr)

您还可以使用 zxdb 中的 handle 命令显示 VMO 信息。

转储“隐藏”(未映射和内核)VMO

k zx vmos hidden

vmos <pid> 类似,但会转储系统中未映射到任何进程的所有 VMO:

  • 用户空间有句柄但未进行映射的 VMO
  • 仅映射到内核空间的 VMO
  • 仅限内核、未映射且没有句柄的 VMO

koid 值为零表示只有内核具有对该 VMO 的引用。

#map 值为零表示 VMO 未映射到任何地址空间。

另请参阅k zx vmos all,用于转储系统中的所有 VMO。注意:由于内核控制台缓冲区限制,此输出被截断的情况很常见,因此通常最好将 k zx vmos hidden 输出与每个用户进程的 vmaps 转储结合使用。

限制

psvmaps 目前均不会计入:

  • 未映射的 VMO 或 VMO 子范围。例如,您可以创建 VMO,向其中写入 1G 数据,这不会在此处显示。

所有进程转储工具均不会考虑以下事项:

  • 成倍映射的网页。如果您使用 VMO 的同一范围创建多个映射,则 VMO 的所有已提交页面都将按照这些页面的映射次数进行计数。这可以在同一进程内,也可以位于进程之间(如果这些进程共享 VMO)。

    请注意,“多重映射页面”包括写入时复制。

  • 进程分配的资源的底层内核内存开销。 例如,一个进程可以有一百万个打开的句柄,而这些句柄会消耗内核内存。

    您可以使用 k zx ps 命令查看进程句柄消耗情况;运行 k zx ps help 可查看其列的说明。

  • 写入时复制 (COW) 克隆 VMO。克隆的干净(非脏、非复制)页面不会计入映射克隆的进程的“共享”页面,而相同的页面可能会错误地计入映射父级(克隆)VMO 的进程的“私有”页面。

    TODO(dbort):解决此问题;工具是在 COW 克隆存在之前编写的。

内核内存

转储系统内存区域和内核堆使用情况

运行 kstats -m 将持续转储有关物理内存用量和可用性的信息。

$ kstats -m
--- 2017-06-07T05:51:08.021Z ---
mem total      free      VMOs     kheap     kfree     wired       mmu       ipc     other
    2048M   1686.4M    317.8M      5.1M      0.9M     17.8M     20.0M      0.1M      0.0M

--- 2017-06-07T05:51:09.021Z ---
...

字段:

  • -t 选项以 ISO 8601 字符串的形式显示收集统计信息时的时间戳 2017-06-07T05:51:08.021Z
  • total:系统可用的物理内存总量。
  • free:未分配内存量。
  • VMOs:提交给 VMO(包括内核和用户)的内存量。所有用户空间内存的超集。不包括属于 wired 的特定 VMO。
  • kheap:标记为分配的内核堆内存量。
  • kfree:标记为可用的内核堆内存量。
  • wired:出于此结构体中的其他字段未涵盖的原因,预留并映射到内核的内存量。通常用于 RAM 磁盘和内核映像等只读数据以及前期启动动态内存。
  • mmu:用于架构专用的 MMU 元数据(如页面表格)的内存量。
  • ipc:进程间通信使用的内存量。
  • otherother 中的其他所有实体。

转储内核地址空间

k zx asd kernel

转储内核的 VMAR/映射/VMO 层次结构,类似于用户进程的 vmaps 工具。

$ k zx asd kernel
as 0xffffffff80252b20 [0xffffff8000000000 0xffffffffffffffff] sz 0x8000000000 fl 0x1 ref 71 'kernel'
  vmar 0xffffffff802529a0 [0xffffff8000000000 0xffffffffffffffff] sz 0x8000000000 ref 1 'root'
    map 0xffffff80015f89a0 [0xffffff8000000000 0xffffff8fffffffff] sz 0x1000000000 mmufl 0x18 vmo 0xffffff80015f8890/k0 off 0 p ages 0 ref 1 ''
      vmo 0xffffff80015f8890/k0 size 0 pages 0 ref 1 parent k0
    map 0xffffff80015f8b30 [0xffffff9000000000 0xffffff9000000fff] sz 0x1000 mmufl 0x18 vmo 0xffffff80015f8a40/k0 off 0 pages 0 ref 1 ''
      object 0xffffff80015f8a40 base 0x7ffe2000 size 0x1000 ref 1
    map 0xffffff80015f8cc0 [0xffffff9000001000 0xffffff9000001fff] sz 0x1000 mmufl 0x1a vmo 0xffffff80015f8bd0/k0 off 0 pages 0 ref 1 ''
      object 0xffffff80015f8bd0 base 0xfed00000 size 0x1000 ref 1
...