1

我想使用“top”工具来分析进程的内存消耗和可能的内存泄漏。为此,我编写了这个程序(程序名称:memoryTest):

int main(){
char* q;
for(int i=0; i<100; i++){
    q = (char*) malloc(1024); 
    sleep(1);
}
return 0;   

}

使用 top 我现在可以通过在所述过程之后使用选项“o”和过滤器规范“COMMAND = memoryTest”进行过滤来观看该程序,但是我看不到该进程的内存消耗没有变化。我这里有一个愚蠢的错误吗?

4

3 回答 3

1

从 malloc 手册页:

通常,malloc() 从堆中分配内存,并根据需要调整堆的大小,使用 sbrk(2)。当分配大于 MMAP_THRESHOLD 字节的内存块时,glibc malloc() 实现使用 mmap(2) 将内存分配为私有匿名映射。MMAP_THRESHOLD 默认为 128 kB,但可以使用 mallopt(3) 进行调整。在 Linux 4.7 之前,使用 mmap(2) 执行的分配不受 RLIMIT_DATA 资源限制的影响;自 Linux 4.7 起,此限制也适用于使用 mmap(2) 执行的分配。

内存池称为 arenas,实现在 arena.c 中。宏 HEAP_MAX_SIZE 定义了 arena 的最大大小,基本上 32 位为 1MB,64 位为 64MB:

HEAP_MAX_SIZE = (2 * DEFAULT_MMAP_THRESHOLD_MAX)
32-bit [DEFAULT_MMAP_THRESHOLD_MAX = (512 * 1024)] = 1,048,576 (1MB)
64-bit [DEFAULT_MMAP_THRESHOLD_MAX = (4 * 1024 * 1024 * sizeof(long))] = 67,108,864 (64MB)

来自堆实现的信息(arena.c):

/* 堆是持有(可合并)malloc_chunks 的单个连续内存区域。它是用 mmap() 分配的,并且总是从与 HEAP_MAX_SIZE 对齐的地址开始。*/

编辑:

可以使用strace观察堆分配。在第一次调用 brk() 时,主 arena 分配了 200K 字节(libstdc++ 中的 72K 和 128K top_pad )。

brk(NULL)                               = 0x556ecb423000 -> current program break
brk(0x556ecb455000)                     = 0x556ecb455000 -> resize the heap by moving brk 0x32000 bytes upward (main arena initialization with 200K). 
write(1, "i = 0\n", 8)                = 8
...
write(1, "i = 123\n", 8)                = 8     
brk(0x556ecb476000)                     = 0x556ecb476000 -> resize the heap by moving brk 0x21000 bytes upward (growing heap 128K). 
...
write(1, "i = 252\n", 8)                = 8
brk(0x556ecb497000)                     = 0x556ecb497000 -> resize the heap by moving brk 0x21000 bytes upward (growing heap 128K). 

您的应用程序仅使用了 100K 字节的 128K 可用堆,因此 top 或 htop 程序不会观察到内存消耗。

如果您通过请求大于 128K 的块或通过增加块数 ( > 128 ) 来强制 glibc 使用 mmap() ,您可以很容易地看到内存消耗的变化。

于 2018-12-05T16:42:20.180 回答
0

如果您想了解内存使用情况,请不要使用 top。使用免费的开源程序https://github.com/vmware/chap (免责声明:我是原始开发者)。

只需在程序完成之前收集一个实时内核,例如通过运行 gcore,然后键入:

第一章你的核心文件名

然后您可以执行以下操作:

计数泄漏列表泄漏计数使用计数免费....

这是一个使用您的程序的示例,在 30 秒后收集核心:

-bash-4.1$ ./q53633998 &
[1] 18014
-bash-4.1$ sleep 30
gcore 18014
-bash-4.1$ gcore 18014
0x00000030ed6aca20 in __nanosleep_nocancel () at ../sysdeps/unix/syscall-         template.S:82
82      T_PSEUDO (SYSCALL_SYMBOL, SYSCALL_NAME, SYSCALL_NARGS)
Saved corefile core.18014
-bash-4.1$ ~/public_html/chap core.18014
chap> count used
36 allocations use 0x9120 (37,152) bytes.
chap> count free
1 allocations use 0x17db0 (97,712) bytes.
chap> count leaked
35 allocations use 0x8d18 (36,120) bytes.
chap> count anchored
1 allocations use 0x408 (1,032) bytes.
chap> list anchored
Used allocation at 19d4e40 of size 408

1 allocations use 0x408 (1,032) bytes.
chap> explain 19d4e40
Address 19d4e40 is at offset 0 of
an anchored allocation at 19d4e40 of size 408
Allocation at 19d4e40 appears to be directly anchored from at least one stack.
Address 0x7ffc88570270 is on the live part of the stack for thread 1.
Stack address 7ffc88570270 references 19d4e40

chap> list leaked
Used allocation at 19cc010 of size 408

Used allocation at 19cc420 of size 408

Used allocation at 19cc830 of size 408

Used allocation at 19ccc40 of size 408

Used allocation at 19cd050 of size 408

Used allocation at 19cd460 of size 408

Used allocation at 19cd870 of size 408

Used allocation at 19cdc80 of size 408

Used allocation at 19ce090 of size 408

Used allocation at 19ce4a0 of size 408

Used allocation at 19ce8b0 of size 408

Used allocation at 19cecc0 of size 408

Used allocation at 19cf0d0 of size 408

Used allocation at 19cf4e0 of size 408

Used allocation at 19cf8f0 of size 408

Used allocation at 19cfd00 of size 408

Used allocation at 19d0110 of size 408

Used allocation at 19d0520 of size 408

Used allocation at 19d0930 of size 408

Used allocation at 19d0d40 of size 408

Used allocation at 19d1150 of size 408

Used allocation at 19d1560 of size 408

Used allocation at 19d1970 of size 408

Used allocation at 19d1d80 of size 408

Used allocation at 19d2190 of size 408

Used allocation at 19d25a0 of size 408

Used allocation at 19d29b0 of size 408

Used allocation at 19d2dc0 of size 408

Used allocation at 19d31d0 of size 408

Used allocation at 19d35e0 of size 408

Used allocation at 19d39f0 of size 408

Used allocation at 19d3e00 of size 408

Used allocation at 19d4210 of size 408

Used allocation at 19d4620 of size 408

Used allocation at 19d4a30 of size 408

35 allocations use 0x8d18 (36,120) bytes.
chap> list free
Free allocation at 19d5250 of size 17db0

1 allocations use 0x17db0 (97,712) bytes.
chap>

最后一个“空闲”分配是在第一次 malloc 调用期间分配的内存块的尾部,并随着随后的 malloc 调用逐渐被分割。

当然,还有其他工具(例如 valgrind)通过检测进程以不同方式工作,但是如果您想要一个可以在不改变进程运行方式的情况下分析进程的内存使用情况的工具,那么 chap 是一个不错的选择。

于 2018-12-06T00:43:46.487 回答
0

尝试以下操作:

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

int main(){
  char* q;
  for(int i=0; i<10000; i++){
    q = (char*)malloc(1024); 
    memset(q,0,1024);
  }
  getchar();
  return 0;
}

for循环中的不同值。

这里的问题是 Linux 不需要使用内存,即使它是在实际填充之前分配的。因此,您需要将数据写入要分配的内容,否则它甚至可能不会记录该内存正在使用中。这就是为什么有的应用程序可以分配内存,这很好,然后即使分配了,当他们使用内存时,他们会发现内存不可用。

memset 将强制将零写入分配的缓冲区,从而导致内存被使用并注册为在顶部使用。请注意,在这里使用 htop 可能更容易。

如果您想进一步了解作为 Linux 特性的“乐观 malloc”,请注意其他一些操作系统的行为方式并非如此。

另外值得指出的是,内部内存是按一定大小的连续块分配的。因此,如果以 4k 的最小块大小分配内存,那么分配额外的 1KB 可能不会增加大小。

于 2018-12-05T15:23:08.613 回答