gcore
附加gdb
到进程,遍历进程的大多数虚拟内存区域并将它们转储到磁盘。这是否意味着每个匿名虚拟内存都需要分页到该进程的内存中,从而增加其 RSS,还是内存分页到进程中gdb
?我猜它也会分页到任何文件支持的内存中(虽然我猜这不应该增加 RSS,但它可能会通过文件缓存增加 RAM 使用量)。
来自 Kubernetes 环境的示例显示 RSS 从 304368 跳转到 17135624(gcore
从工作节点调试 pod 运行):
# ps auxwww | head -1
USER PID %CPU %MEM VSZ RSS TTY STAT START TIME COMMAND
# ps auxwww | grep 3899524 | grep -v grep
1000650+ 3899524 0.2 0.9 17229416 304368 ? SLsl Jun13 54:01 /opt/java/openjdk/jre/bin/java [...]
# gcore 3899524
[...]
# ps auxwww | grep 3899524 | grep -v grep
1000650+ 3899524 0.2 53.3 17229416 17135624 ? SLsl Jun13 54:01 /opt/java/openjdk/jre/bin/java [...]
这可能与容器/cgroups 具体相关吗?
答案1
将进程的大部分内存写入文件(作为核心转储)需要读取它。具体如何实现取决于操作系统。
在大多数 POSIX 系统上,进程(或可能是其分支)会被告知转储核心,无论是通过类似调试器gcore
还是在特定条件下终止的任务。这种方式效率更高,否则可能需要将内存从一个地址空间复制到另一个地址空间。
在 Linux 上,man core
记录转储的内容。/proc/[pid]/coredump_filter
包含一个位掩码,允许对此进行自定义。请注意,默认设置是几种匿名或私有映射。默认情况下,文件映射被排除,因为它们可以在任务结束后从文件中读取。
请注意,在 ps 输出中,驻留集大小 (RSS) 在转储时接近虚拟内存大小 (VSZ)。我假设一个具有 16 GB 内存且内存发生变化的 Java 程序将大部分内存放在堆中,可能静态调整大小以供生产使用。完整转储将涉及每个页面,即使其中大部分仍为未受影响的零。
这并不一定意味着从交换区调入页面。虽然这种情况可能发生,但您提供的信息并未提及交换区。
对如此大的进程进行核心转储会产生很大的开销。无论是 CPU 循环遍历所有内存,还是存储将其写出,都是如此。尽可能避免这样做,而应采用开销较小的分析和调试方法。
Linux cgroups 对此没有太大改变。主要是为您的容量规划提供更具体的核算。特定容器或 systemd 单元的内存使用量近似于它可能创建的核心转储的大小。另请参阅systemd-cgtop -m