【发布时间】:2022-01-26 06:31:33
【问题描述】:
如何以编程方式检测堆上进程分配的字节数? 这个测试应该从进程本身开始。
【问题讨论】:
-
为什么?如果我们知道原因,那么推荐一些东西可能比抛弃现有的百万加一(危险)技术更容易。
-
我有内存泄漏,valgrind 找不到。
如何以编程方式检测堆上进程分配的字节数? 这个测试应该从进程本身开始。
【问题讨论】:
我认为 mallinfo() 是你想要的:
#include <malloc.h>
struct mallinfo *info;
info = mallinfo();
printf ("total allocated space: %llu bytes\n", info->uordblks);
printf ("total free space: %llu bytes\n", info->fordblks);
struct mallinfo 结构是技术性的,特定于 malloc() 实现。但是你想要的信息就在那里。以下是我报告值的方式:
mallinfo.arena = "Total Size (bytes)"
mallinfo.uordblks = "Busy size (bytes)"
mallinfo.fordblks = "Free size (bytes)"
mallinfo.ordblks = "Free blocks (count)"
mallinfo.keepcost = "Top block size (bytes)"
mallinfo.hblks = "Blocks mapped via mmap() (count)"
mallinfo.hblkhd = "Bytes mapped via mmap() (bytes)"
据称这两个没有被使用,但它们似乎在我的系统上发生了变化,因此可能是有效的:
mallinfo.smblks = "Fast bin blocks (count)"
mallinfo.fsmblks = "Fast bin bytes (bytes)"
另一个有趣的值是由“sbrk(0)”返回的
【讨论】:
有很多可能性。
您需要它有多准确?您可以通过 cat /proc/${PID}/status | 获得一些有用的数据。 grep VmData.
您可以#define您自己的malloc()、realloc()、calloc()和free() 函数,将真正的函数封装在你自己的计数器后面。您可以在这里使用 __FILE__、__LINE__ 和 __func__ 做一些非常酷的事情,以便在简单测试中识别核心泄漏。 但它只会检测您自己的代码!
(同样,您也可以重新定义默认的 operator new 和 operator delete 方法,包括数组和非数组变体,并且都抛出 std::bad_alloc 和 std ::nothrow_t 变体。同样,这只会检测您自己的代码!)
(注意:在大多数 C++ 系统上,new 最终会调用 malloc()。它不必这样做。尤其是在原地 new!但通常 new 确实会使用 malloc()。(或者它在之前已 malloc()'ed 的内存区域上运行.) 否则你会遇到多个堆管理器的非常时髦的东西...)
您可以使用 sbrk(0) 查看当前设置数据段的位置。那不是很好。这是一个非常粗略的测量,它没有考虑堆中的漏洞(未使用的内存区域)。 (使用 /proc/${PID}/status 中的 VmData 行会更好。)但是如果您只是在寻找一个总体思路.. .
您可以通过编写自己的共享库并通过 LD_PRELOAD 强制您的进程使用它而不是真实版本来捕获 malloc()/free()/etc。您可以使用 dlopen()/dlsym() 来加载和调用 *real* malloc()/free()/etc。这非常漂亮。原始代码未修改,甚至未重新编译。但是在编写这个库时要注意重入的情况,你的进程将在 dlopen()/dlsym()malloc()/calloc()/realloc() /em> 可以完成加载真正的功能。
您可能会查看 Valgrind 之类的工具,尽管这实际上更多地针对内存泄漏。
再一次,也许 mtrace() 是你想要的?还是 __malloc_hook?非常专有(GNU)和非标准...但是您被标记为“Linux”...
【讨论】:
没有简单的自动方法来做到这一点,如果那是你的要求。您基本上必须使用计数器变量自己手动跟踪堆分配。问题是很难控制程序的哪些部分在堆上分配内存,尤其是当您使用大量不受您控制的库时。更复杂的是,程序可能有两种分配堆内存的方式:new 或malloc。 (更不用说像sbrk 这样的直接操作系统调用。)
您可以override global operator new,并且每次调用 new 都会增加一个全局计数。但是,这不一定包括您的程序调用malloc 的时间,或者您的程序使用某些特定于类的new 覆盖的时间。您也可以使用宏覆盖malloc,但这不一定是可移植的。而且您还必须覆盖malloc 的所有变体,例如realloc、calloc 等。所有这些都因为在某些实现中new 本身可能调用malloc 而变得更加复杂。 .
因此,从本质上讲,在您的程序中正确执行此操作非常困难。我建议改用内存分析器工具。
【讨论】:
推测性解决方案:重新定义 new 和 delete 运算符。
在每个new 运算符调用中,都会传递要分配的字节数。分配更多内存并存储其中分配的字节数。将此数量添加到保存堆大小的全局变量中。
在delete 操作员调用时,在处置内存之前检查您存储的值。从该全局变量中减去它。
【讨论】:
如果您使用的是 Windows,则可以使用 GetProcessHeap()、HeapQueryInfo() 检索有关进程堆的信息。 An example of walking the heap from MSDN
【讨论】:
由于您已将问题标记为“linux”,因此查看/proc 目录中提供的一些信息可能会有所帮助。我没有对此进行大量研究,所以我只能给你一个起点。
/proc/<your programs pid> 包含一些文件,其中包含从内核的角度来看有关您的进程的一些信息。有一个符号链接 /proc/self 将始终与您调查此问题的过程有关。
您可能最感兴趣的文件是stat、statm 和status。后者更易于人类阅读,而前两者以更机器可读的格式提供相同的信息。
proc(5) 手册页中提供了有关如何解释这些文件内容的起点。
【讨论】:
然后跟踪所有内存分配我不相信有一种方法可以计算堆大小或使用情况
【讨论】:
使用malloc_info()。享受它的 XML 方面的乐趣。 mallinfo()(如另一个答案所示)做了类似的事情,但仅限于 32 位值......在 2010 年+ 依赖于愚蠢的事情。
【讨论】: