如何衡量Linux中应用程序或进程的内存使用情况?

在博客文章《了解Linux上的内存使用情况》中,ps并不是一个准确的工具。

为什么ps是“错误的”根据您的看法,ps不会报告进程的实际内存使用情况。它真正要做的是显示如果只有一个进程在运行,每个进程将占用多少实际内存。当然,一台典型的Linux机器在任何给定时间都有几十个进程在运行,这意味着ps报告的VSZ和RSS数字几乎肯定是错误的。

(注:这里详细介绍了这个问题。)


当前回答

这里再次投票支持Valgrind,但我想补充一点,您可以使用Alleyoop这样的工具来帮助您解释Valgrind生成的结果。

我一直使用这两种工具,并且总是有简洁、无泄漏的代码来自豪地展示它;)

其他回答

Valgrind可以显示详细信息,但它会显著降低目标应用程序的速度,而且大多数时候它会改变应用程序的行为。

Exmap是我还不知道的东西,但似乎需要一个内核模块来获取信息,这可能是一个障碍。

我假设每个人都想知道关于“内存使用”的以下内容。。。在Linux中,单个进程可能使用的物理内存量大致可分为以下几类。

M.匿名映射内存-p专用.d dirty==malloc/mma映射的堆和堆栈分配和写入的内存.c clean==分配、写入、然后释放但尚未回收的malloc/mma映射的堆和堆栈内存.s已共享.d dirty==malloc/maped堆可以在写入时获得副本并在进程之间共享(已编辑).cclean==malloc/maped堆可以在写入时获得副本,并在进程之间共享(已编辑)M.n命名映射内存-p专用.d dirty==文件映射的写入内存专用.c clean==映射的程序/库文本专用映射.s已共享.d dirty==文件映射的写入内存共享.c clean==映射的库文本共享映射

Android中包含的工具showmap非常有用

virtual                    shared   shared   private  private
size     RSS      PSS      clean    dirty    clean    dirty    object
-------- -------- -------- -------- -------- -------- -------- ------------------------------
       4        0        0        0        0        0        0 0:00 0                  [vsyscall]
       4        4        0        4        0        0        0                         [vdso]
      88       28       28        0        0        4       24                         [stack]
      12       12       12        0        0        0       12 7909                    /lib/ld-2.11.1.so
      12        4        4        0        0        0        4 89529                   /usr/lib/locale/en_US.utf8/LC_IDENTIFICATION
      28        0        0        0        0        0        0 86661                   /usr/lib/gconv/gconv-modules.cache
       4        0        0        0        0        0        0 87660                   /usr/lib/locale/en_US.utf8/LC_MEASUREMENT
       4        0        0        0        0        0        0 89528                   /usr/lib/locale/en_US.utf8/LC_TELEPHONE
       4        0        0        0        0        0        0 89527                   /usr/lib/locale/en_US.utf8/LC_ADDRESS
       4        0        0        0        0        0        0 87717                   /usr/lib/locale/en_US.utf8/LC_NAME
       4        0        0        0        0        0        0 87873                   /usr/lib/locale/en_US.utf8/LC_PAPER
       4        0        0        0        0        0        0 13879                   /usr/lib/locale/en_US.utf8/LC_MESSAGES/SYS_LC_MESSAGES
       4        0        0        0        0        0        0 89526                   /usr/lib/locale/en_US.utf8/LC_MONETARY
       4        0        0        0        0        0        0 89525                   /usr/lib/locale/en_US.utf8/LC_TIME
       4        0        0        0        0        0        0 11378                   /usr/lib/locale/en_US.utf8/LC_NUMERIC
    1156        8        8        0        0        4        4 11372                   /usr/lib/locale/en_US.utf8/LC_COLLATE
     252        0        0        0        0        0        0 11321                   /usr/lib/locale/en_US.utf8/LC_CTYPE
     128       52        1       52        0        0        0 7909                    /lib/ld-2.11.1.so
    2316       32       11       24        0        0        8 7986                    /lib/libncurses.so.5.7
    2064        8        4        4        0        0        4 7947                    /lib/libdl-2.11.1.so
    3596      472       46      440        0        4       28 7933                    /lib/libc-2.11.1.so
    2084        4        0        4        0        0        0 7995                    /lib/libnss_compat-2.11.1.so
    2152        4        0        4        0        0        0 7993                    /lib/libnsl-2.11.1.so
    2092        0        0        0        0        0        0 8009                    /lib/libnss_nis-2.11.1.so
    2100        0        0        0        0        0        0 7999                    /lib/libnss_files-2.11.1.so
    3752     2736     2736        0        0      864     1872                         [heap]
      24       24       24        0        0        0       24 [anon]
     916      616      131      584        0        0       32                         /bin/bash
-------- -------- -------- -------- -------- -------- -------- ------------------------------
   22816     4004     3005     1116        0      876     2012 TOTAL

如果您的代码是C或C++,您可能可以使用getrusage(),它会返回有关进程内存和时间使用情况的各种统计信息。

但并非所有平台都支持这一点,并将为内存使用选项返回0值。

相反,您可以查看在/proc/[pid]/statm中创建的虚拟文件(其中[pid]被您的进程id替换)。

此文件看起来像一个包含7个整数的文本文件。您可能对该文件中的第一个(所有内存使用)和第六个(数据内存使用)数字最感兴趣。

使用ps或类似工具,您只能获得该进程分配的内存页数量。此数字正确,但:

不反映应用程序使用的实际内存量,只反映为其保留的内存量如果页面被共享,例如由多个线程共享或使用动态链接库共享,则可能会产生误导

如果您真的想知道应用程序实际使用的内存量,则需要在探查器中运行它。例如,Valgrind可以让您了解所使用的内存量,更重要的是,了解程序中可能的内存泄漏。Valgrind的堆轮廓仪工具称为“massif”:

Massif是一个堆分析器。它通过定期拍摄程序堆的快照来执行详细的堆分析。它会生成一个图表,显示一段时间内的堆使用情况,包括程序中哪些部分负责最多内存分配的信息。该图由文本或HTML文件补充,该文件包含更多信息,用于确定分配最多内存的位置。Massif运行程序的速度比正常速度慢20倍。

如Valgrind文档中所述,您需要通过Valgrind运行程序:

valgrind --tool=massif <executable> <arguments>

Massif写入内存使用快照的转储(例如Massif.out.1245)。这些快照提供(1)内存使用的时间线,(2)每个快照的程序内存分配位置的记录。分析这些文件的一个很好的图形工具是massif可视化工具。但我发现,与Valgrind一起提供的基于文本的简单工具ms_print已经有了很大的帮助。

要查找内存泄漏,请使用valgrind的(默认)memcheck工具。

如果进程没有占用太多内存(可能是因为您希望是这样,或者其他命令给出了这个初始指示),并且进程可以承受短时间的停止,您可以尝试使用gcore命令。

gcore <pid>

检查生成的核心文件的大小,以了解特定进程正在使用多少内存。

如果进程使用数百兆字节或千兆字节,这将不会太好,因为根据I/O性能,核心生成可能需要几秒钟或几分钟才能创建。在核心创建过程中,进程被停止(或“冻结”)以防止内存更改。所以要小心。

还要确保生成核心的装载点有足够的磁盘空间,并且系统不会对在该特定目录中创建的核心文件做出负面反应。

除了答案中列出的解决方案之外,您还可以使用Linux命令“top”。它提供了运行系统的动态实时视图,并给出了整个系统的CPU和内存使用情况,以及每个程序的百分比:

top

通过程序PID进行过滤:

top -p <PID>

按程序名过滤:

top | grep <PROCESS NAME>

“顶部”还提供一些字段,例如:

VIRT—虚拟映像(kb):任务使用的虚拟内存总量

RES——驻留大小(kb):任务使用的非交换物理内存;RES=代码+数据。

DATA——数据+堆栈大小(kb):除可执行代码外,专用于其他代码的物理内存量,也称为“数据驻留集”大小或DRS。

SHR—共享内存大小(kb):任务使用的共享内存量。它只是反映了可能与其他进程共享的内存。

此处参考。