4

我在 Suse Linux Enterprise 上运行我的 C 程序,该程序压缩了数千个大文件(大小在 10MB 到 100MB 之间),并且随着程序运行,程序变得越来越慢(它在 Intel Sandy Bridge 板上以 32 个线程运行多线程)。当程序完成并再次运行时,它仍然很慢。

当我看到程序运行时,我看到程序运行时内存正在耗尽,你会认为这只是一个典型的内存泄漏问题。但是,对于正常的 malloc()/free() 不匹配,我希望在程序终止时所有内存都会返回。但是,当程序完成时,大部分内存都不会被回收。free 或 top 命令显示 Mem: 63996M total, 63724M used, 272M free 当程序减速到停止时,但在终止后,空闲内存只增长到大约 3660M。当程序重新运行时,空闲内存很快就用完了。

上面的程序只显示该程序在运行时最多使用 4% 左右的内存。

我认为这可能是一个内存碎片问题,但是,我构建了一个小型测试程序来模拟程序中的所有内存分配活动(许多随机方面是内置的 - 大小/数量),它总是返回所有内存完成。所以,我不认为是这样。

问题:

  1. 是否存在会永久丢失内存的 malloc()/free() 不匹配,即即使在进程完成之后?

  2. C 程序(不是 C++)中的哪些其他内容会导致永久内存丢失,即在程序完成后,甚至终端窗口关闭?只有重新启动才能恢复内存。我读过其他关于文件未关闭导致问题的帖子,但是,我认为我没有这个问题。

  3. 查看内存统计信息的顶部和空闲是否有效,即它们是否准确地描述了内存情况?它们似乎确实对应于程序的缓慢性。

  4. 如果程序只显示 4% 的内存使用,那么 valgrind 之类的会发现这个问题吗?

4

5 回答 5

5

是否存在会永久丢失内存的 malloc()/free() 不匹配,即即使在进程完成之后?

不,free,甚至在这方面是无害的,并且当进程终止操作系统(在这种情况下为 SUSE Linux)时,会收回所有内存(除非它与仍在运行的其他进程共享)。

C 程序(不是 C++)中的哪些其他内容会导致永久内存丢失,即在程序完成后,甚至终端窗口关闭?只有重新启动才能恢复内存。我读过其他关于文件未关闭导致问题的帖子,但是,我认为我没有这个问题。

与 malloc/free 和 mmap 一样,进程打开的文件会被操作系统自动关闭。

有一些事情会导致永久性内存泄漏,例如大页面,但如果您使用它们,您肯定会知道。除此之外,没有。


但是,如果您将内存丢失定义为没有立即标记为“空闲”的内存,那么可能会发生一些事情。

  1. 对磁盘或 mmap 的写入可能会在 RAM 中缓存一段时间。操作系统必须保留这些页面,直到将它们同步回磁盘。
  2. 如果操作系统现在没有其他东西可以使用该 RAM,则进程读取的文件可能会保留在内存中 - 合理的假设是它可能很快需要它们并且读取已经在 RAM 中的副本会更快。同样,如果操作系统或其他进程需要一些 RAM,则可以立即将其丢弃。

请注意,作为为我所有的 RAM 付费的人,我宁愿操作系统一直使用它,如果它以最小的方式有帮助的话。空闲 RAM 是浪费的 RAM。


几乎没有可用 RAM 的主要问题是当它被过度使用时,也就是说,现在有更多的进程(和操作系统)要求或使用 RAM,而不是系统上可用的。听起来您在进程中使用了大约 4Gb 的 RAM,这可能是个问题 - (请记住操作系统也需要一个好块。但听起来您有足够的 RAM!尝试运行一半的进程并查看如果它变得更好。

有时内存泄漏会导致暂时的过度使用——调查一下是个好主意。尝试随着时间的推移绘制程序的内存使用情况 - 如果它持续上升,那么它很可能是泄漏。

请注意,fork一个进程会创建一个副本,该副本共享原始分配的内存 - 直到两者都关闭或其中一个'exec'。但你没有那样做。

查看内存统计信息的顶部和空闲是否有效,即它们是否准确地描述了内存情况?它们似乎确实对应于程序的缓慢性。


是的,top并且ps是查看内存的完全合理的方法,特别是观察 RES 字段。暂时忽略 VIRT 字段。此外:

要查看整个系统对内存的使用情况,请运行:

vmstat 10

在您的程序运行时以及一段时间后。看看---memory---列发生了什么。

此外,在您的过程完成后,运行

cat /proc/meminfo

并在您的问题中发布结果。

如果程序只显示 4% 的内存使用,那么 valgrind 之类的会发现这个问题吗?

可能,但它可能非常慢,在这种情况下这可能是不切实际的。还有许多其他工具可以提供帮助,例如电子围栏和其他不会显着降低程序速度的工具。过去我什至推出了自己的产品。

于 2012-09-08T03:29:22.050 回答
3

malloc()/free() 在堆上工作。当进程终止时,保证会将此内存释放给操作系统。即使在分配过程使用某些共享内存原语(例如 System V IPC)终止后,内存泄漏也是可能的。但是,我认为这些都没有直接关系。

退后一步,这是一个轻负载 Linux 服务器的输出:

$ uptime
 03:30:56 up 72 days,  8:42,  2 users,  load average: 0.06, 0.17, 0.27
$ free -m
             total       used       free     shared    buffers     cached
Mem:         24104      23452        652          0      15821        978
-/+ buffers/cache:       6651      17453
Swap:         3811          5       3806

哦不,只有 652 MB 可用!正确的?错误的。

每当 Linux 访问块设备(例如,硬盘驱动器)时,它都会查找任何未使用的内存,并将数据的副本存储在那里。毕竟,为什么不呢?数据已经在 RAM 中,某些程序显然想要该数据,而未使用的 RAM 对任何人都没有任何好处。如果一个程序出现并要求更多内存,缓存的数据将被丢弃以腾出空间——在那之前,还不如挂在它上面。

这个free输出的关键不是第一行,而是第二行。是的,正在使用 23.4 GB 的 RAM - 但 17.4 GB 可用于需要它的程序。见帮助!Linux 吃掉了我的内存!更多。

我不能说为什么程序变慢了,但是“可用内存”指标稳步下降到零是完全正常的,而不是原因。

于 2012-09-08T03:40:49.077 回答
1

操作系统只释放它绝对需要的内存。如果稍后正常使用内存,则释放内存是浪费精力 - 直接将内存从一种用途转移到另一种用途比释放内存只是为了以后必须使其不自由更有效。

系统唯一需要空闲内存的是需要内存的操作,这些操作不能将已使用的内存从一个目的切换到另一个目的。这是一组非常小的异常操作,例如服务网络中断。

如果您键入此命令sysctl vm.min_free_kbytes,系统将告诉您它需要空闲的 KB 数。它可能小于 100MB。因此,拥有超过免费数量的任何金额都很好。

如果您想要更多可用内存,请将其从计算机中删除。否则,操作系统假定使用它的成本为零,因此免费提供零收益。

例如,考虑您写入磁盘的数据。操作系统可以释放保存该数据的内存。但这是双重损失。如果您写入磁盘的数据稍后被读取,它将不得不从磁盘读取它,而不仅仅是从内存中获取它。如果以后需要该内存用于其他目的,它只需要撤消它所经历的所有工作,使其免费。呸。因此,如果系统不是绝对需要空闲内存,它就不会释放它。

于 2012-09-08T03:25:22.817 回答
0

我的猜测是问题不在于您的程序,而在于操作系统。假设您将再次访问它们,操作系统会将最近使用的文件缓存在内存中。它不确定需要哪些文件,因此它最终可能会决定保留错误的文件,而以牺牲您希望保留的文件为代价。

当您进行第二次运行时,它可能会保留第一次运行的输出文件,这会阻止它在第二次运行时有效地使用缓存。您可以通过从第一次运行中删除所有文件(这应该将它们从缓存中释放)并查看这是否会使第二次运行更快来测试这个理论。

如果这不起作用,请尝试删除第一次运行的所有输入文件。

于 2012-09-08T11:21:51.540 回答
-1

答案

  1. 是的,C 或 C++ 中不需要释放未释放回操作系统的内存
  2. 您是否有内存映射文件、已删除文件的打开文件句柄等。Linux 不会删除文件,直到所有引用都被解除分配。此外,Linux 会将文件缓存在内存中,以防需要再次读取 - 文件缓存内存使用可以忽略,因为操作系统会处理它
  3. 也许 valgrind 会突出显示内存不足的情况
于 2012-09-08T03:09:18.260 回答