查找大块已分配的内存

4
我有一个用C/C++编写的程序(守护进程)。它运行得很好,但是在一段时间后(可能是5天、一周或两周),它开始分配大量内存。我无法理解哪些代码部分没有释放已分配的内存。启动时内存使用量约为20-30兆字节。然后经过一段时间,或者可能是某个事件后,它缓慢增长1MB每小时,如果不终止就会崩溃,因为没有可用内存。
我尝试使用Valgrind,并在守护进程已经分配了大约500MB内存时正常关闭它。关闭过程非常漫长,但完成后,Valgrind表示未发现任何内存泄漏,除了mysql_init/mysql_close过程(明确丢失了约504字节)。谷歌说不用担心这个Mysql泄漏,并给出了一些原因,为什么像Valgrind这样的内存诊断工具认为它是泄漏。
我真的不知道哪些代码部分会分配内存,但只有在程序关闭时才释放它。请帮助我找出这个问题。

你的代码可能不会传统意义上的泄漏内存,但这些类型的资源问题时常出现,即使你对代码非常熟悉,也很难找到。如果没有看到你的代码,就无法确定发生了什么。 - Chad
2
Valgrind的工具Massif在这种情况下可能比默认工具Memcheck更有用。 - ninjalj
3个回答

4
Valgrind只能检测未被删除的指针,保留不需要的指针是另一个问题。首先,在关闭时释放所有对象和内存。如果有泄漏,valgrind将检测到它作为未被对象引用的内存等。然而,任何泄漏最终都会被操作系统释放。如果你捕获所有异常并且不做任何处理,那么请不要这样做,这是一个常见原因。其次,在关闭期间调用的析构函数的日志文件可能有所帮助。也许在main()结束时设置一个全局标志;当该标志被设置时调用的任何析构函数都可以输出它们存在的信息。看看是否有很多不应该存在的对象。更简单的方法是使用全局变量,每个构造函数都可以将其增加1,析构函数将其减少1。如果发现对象数量不能保持相对稳定,可以使用类似的技术来调查哪些对象造成了问题。第三,使用Boost及其范围智能指针来帮助,但不要依赖于智能指针作为圣杯。我遇到过一个可能的潜在问题。对于长时间运行的程序,内存碎片可能导致大量内存使用。您可以删除1mb对象,然后尝试创建2mb对象;创建将在新空间中进行,因为该1mb“空闲块”不够大。然后,当您创建512kb对象时,它可能会进入该1mb对象的空间,仅使用可用空间的一半,但使得您的下一个1mb对象需要在大空间中分配。不幸的是,由于小对象被分配在持久位置,这个问题可能变得很糟糕。例如,内存中相距300kb的50字节类可能有100个,但是在该空间中无法分配任何512kb对象,因此每个新对象都会分配额外的512kb,即使您的程序已经拥有足够的空间,实际上浪费了90%以上的“自由”空间。如果您检查程序流程,请寻找小型分配。请记住,std::list / vector /等都可能导致这种情况;如果您想运行多次内存操作的守护程序,最好使用reserve()预先分配内存。内存池更好。根据您想要投入的时间,您还可以制作(或查找)自定义内存分配器,在关闭时报告对象。

3
尝试使用Valgrind Massif工具。从Massif手册中了解到:
此外,有一些空间泄漏无法被传统的泄漏检查器(如Memcheck)检测到。这是因为内存实际上并没有丢失——一个指针仍然指向它——但它没有被使用。具有此类泄漏的程序可能会不必要地增加它们随着时间而使用的内存量。Massif可以帮助识别这些泄漏。
Massif应该能够显示内存的使用情况以及在关闭之前分配和未释放的位置。

1

既然你确定没有内存泄漏,那么你的程序可能会分配内存并存储数据而不泄漏。

例如,假设你的程序使用链表...

struct list{
DATA_ARRAY arr; //Some data 
struct *list next;
};

While(true) //infinite loop
{
// Add new nodes to list 
// Store some data in the node
}

这里没有泄漏。但是循环会不断添加新节点并存储数据,一切都是完全有效的。但是内存使用量一直在增加。由于您运行了2-5天,这样的情况肯定是可能的。

如果不再需要,您可能需要检查代码并释放内存。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接