Memleak demo 不获取常驻内存

Memleak demo does not obtain resident memory

我想在 Linux 系统上设置一些每个用户的内存 limits.conf,为了测试我写了一个最小程序:

#include <stdio.h>
#include <stdlib.h>

int main()
{
        size_t mbs = 32;
        char * leak;
        while (1)
        {
//              leak = calloc(1024 * 1024 * mbs, sizeof(char));
                leak = malloc(1024 * 1024 * mbs * sizeof(char));
                printf("%9lu MiB allocated at address %p.", mbs, leak);
//              printf(" Last byte has value %d.", leak[1024 * 1024 * mbs - 1]);
                printf(" Press enter to double them.");
                getchar();
                mbs *= 2;
        }
}

执行有点

$ gcc -g -O0 memleak.c -o memleak  && ./memleak
       32 MiB allocated at address 0x14b92f312010. Press enter to double them.
       64 MiB allocated at address 0x14b92b311010. Press enter to double them.
<.....>
    32768 MiB allocated at address 0x14a933308010. Press enter to double them.
    65536 MiB allocated at address 0x149933307010. Press enter to double them.
   131072 MiB allocated at address (nil). Press enter to double them.
   262144 MiB allocated at address (nil). Press enter to double them.^C

在配备 64 GiB RAM 和 2 GiB 交换空间的系统上 space。你可以发现 mallocs 在分配超过 65536 MiB 后开始失败。

令我困惑的是,泄漏似乎不是“真实的”。我的意思是,在另一个 shell 上,我正在使用

监控进程
$ top -c -p $(pgrep memleak)
    PID USER      PR  NI    VIRT    RES    SHR S  %CPU  %MEM     TIME+ COMMAND                                                                                                                                    
3102203 pbertoni  30  10   35268  33772   1012 S   0,0   0,1   0:00.01 ./memleak  

而且只有VIRT是真正遵循几何级数的。 RES 保持不变:calloc 大约在第一次调用时,~32 MiB。使用 malloc,它下降到 580 字节。

当超过 64 GiB 的物理阈值时,类似数组的取消引用操作会抛出 SIGSEGV,这就是它被评论的原因。

无论如何,我的观点是:为什么我在实际写入时使用“虚拟”内存(例如使用 calloc)?我原本希望取而代之的是“常驻”内存。我错过了什么?

简短的回答是“常驻”内存 运行ges 是进程使用的“虚拟”内存 运行ges 的一个子集,你不能指望页面当前常驻 保持常驻,因为如果您的进程暂时不访问给定的 运行ge,操作系统可以自由地使其部分或部分成为非常驻,以便相应的物理内存可用于别的东西。例如,在您的特定程序中,当它调用 getchar() 时,考虑到它正在等待用户输入,这可能会花费任意长的时间,您的页面可能会在该调用期间的某个时刻变得非常驻。

考虑问题中 top 的输出:

$ top -c -p $(pgrep memleak)
    PID USER      PR  NI    VIRT    RES    SHR S  %CPU  %MEM     TIME+ COMMAND                                                                                                                                    
3102203 pbertoni  30  10   35268  33772   1012 S   0,0   0,1   0:00.01 ./memleak  

注意“VIRT”和“RES”的列。在这种特殊情况下,“VIRT”的数量仅略多于 RES,这意味着进程使用的虚拟地址 space 除了大约 2 MiB (35268KB - 33772KB) 之外的所有内容都驻留在您 运行 顶部。猜测,你 运行 top 在你的程序第一次通过循环调用 getchar() 的时候,但还没有晚到给定的运行ge 在您 运行 top.

时已被推出内存

如果您想回答进程的虚拟地址 space 的哪一部分是非常驻的问题,请再次记住,这可能会根据您对内存的其他需求而改变服务器,您可以通过启动程序并让它到达第一个 getchar() 调用来检查这一点,找出 pid(例如,通过使用来自不同会话的“ps -fe”,然后执行以下操作:

cat /proc/你的进程的pid/smaps >mysmaps

现在编辑 mysmaps 并查找“大小”(运行ge 的虚拟地址大小)与“res”不同的地方

因此,例如,如果您在第一次调用 getchar() 时离开您的程序(通过不提供用户输入)和 运行 其他一些也使用内存的程序,您将看到您的页面将获得推出内存,您可能会在当时创建的 mysmaps 文件中看到如下部分:

7f991fd23000-7f9921d24000 rw-p 00000000 00:00 0
Size:              32772 kB
Rss:                   4 kB
Pss:                   4 kB
Shared_Clean:          0 kB
Shared_Dirty:          0 kB
Private_Clean:         0 kB
Private_Dirty:         4 kB
Referenced:            4 kB
Anonymous:             4 kB
AnonHugePages:         0 kB
Swap:                  0 kB
KernelPageSize:        4 kB
MMUPageSize:           4 kB
Locked:                0 kB
ProtectionKey:         0
VmFlags: rd wr mr mp me ac sd

您可以从中看到,此时您的 32MB 分配不再驻留,因为 activity 其他程序已将其推出内存。