是否可以在POSIX系统上部分释放动态分配的内存

Is it possible to partially free dynamically-allocated memory on a POSIX system?

本文关键字:释放 动态分配 内存 POSIX 系统 是否      更新时间:2023-10-16

我有一个C++应用程序,在该应用程序中,我有时需要将POD类型的大缓冲区(例如,25个b百万float s的数组(一次保存在内存中的连续块中。这个特定的内存组织是由应用程序使用一些对数据进行操作的C API这一事实驱动的。因此,不同的安排(例如std::deque使用的较小内存块的列表(是不可行的。

该应用程序具有以流方式在阵列上运行的算法;这样想:

std::vector<float> buf(<very_large_size>);
for (size_t i = 0; i < buf.size(); ++i) do_algorithm(buf[i]);

该特定算法是应用于数据集的早期处理步骤的流水线的结论。因此,一旦我的算法通过了数组中的第i个元素,应用程序就不再需要它了

因此,理论上,我可以释放内存,以减少应用程序在处理数据时占用的内存。然而,执行类似于realloc()(或std::vector<T>::shrink_to_fit()(的操作将是低效的,因为我的应用程序将不得不在重新分配时花费时间将未使用的数据复制到新的位置。

我的应用程序运行在符合POSIX的操作系统(例如Linux、OSX(上。有没有接口可以让操作系统只从内存块的前面释放指定的区域?这似乎是最有效的方法,因为我只需要通知内存管理器,例如,一旦我完成了内存块的前2GB就可以回收。

如果你的整个缓冲区必须同时在内存中,那么你可能不会从稍后部分释放它中获得太多好处。

这篇文章的要点基本上是不要告诉你做你想做的事情,因为如果实际上不需要的话,操作系统不会不必要地将应用程序的内存保留在RAM中。这就是"常驻内存使用"answers"虚拟内存使用"之间的区别。"常驻"是当前使用的内存,在RAM中,"虚拟"是应用程序的总内存使用量。只要交换分区足够大,"虚拟"内存基本上是没有问题的。[我在这里假设您的系统不会耗尽虚拟内存空间,这在64位应用程序中是正确的,只要您不使用数百TB的虚拟空间!]

如果你仍然想这样做,并且想要有一些合理的可移植性,我建议构建一个"包装器",它的行为有点像std::vector,一次分配几兆字节(或者几千兆字节(的内存块,然后类似于:

 for (size_t i = 0; i < buf.size(); ++i) {
    do_algorithm(buf[i]);
    buf.done(i);
 }

done方法只需检查i(一个元素(的值是否超过当前缓冲区的末尾,并释放它。

如果这能让你有所收获,我会非常惊讶,除非do_algorithm(buf[i])需要相当长的时间(当然是很多秒,可能是很多分钟甚至几个小时(。当然,只有当你真的有其他有用的东西与记忆有关时,它才会有所帮助。即便如此,如果系统内存不足,操作系统也会通过将未被积极使用的内存交换到磁盘来回收内存。

换句话说,如果你分配100GB,填满它,让它坐着不动,它最终会全部在硬盘上,而不是RAM中。

此外,应用程序中的堆保留释放的内存,操作系统在应用程序退出之前不会取回内存,这一点并不罕见。当然,如果只释放了较大分配的一部分,运行时在释放整个块之前不会释放它。所以,正如开头所说,我不确定这对您的应用程序有多大帮助。

与所有关于"调优"answers"性能改进"的内容一样,您需要衡量和比较基准,看看它有多大帮助。

是否可以在POSIX系统上部分释放动态分配的内存?

不能使用malloc()/realloc()/free()执行此操作。

但是,您可以使用mmap()munmap()以半可移植的方式执行此操作。关键是,如果你munmap()某个页面,malloc()以后可以使用该页面:

  • 使用CCD_ 17创建匿名映射
  • 随后为您不再需要的区域调用munmap()

可移植性问题是:

  • POSIX没有指定匿名映射。有些系统提供MAP_ANONYMOUSMAP_ANON标志。其他系统提供可以为此目的映射的特殊设备文件。Linux同时提供这两种功能
  • 我不认为POSIX保证当你munmap()一个页面时,malloc()就能使用它。但我认为它能在所有有mmap()/unmap()的系统中工作

更新

如果您的内存区域太大,以至于大多数页面肯定会被写入交换,那么使用文件映射而不是匿名映射将不会丢失任何内容。文件映射是在POSIX中指定的。

如果你可以不用std::vector的便利(在这种情况下,它不会给你太多好处,因为你永远不想复制/return/移动那个野兽(,你可以自己处理内存。向操作系统索取整页内存(通过mmap(,并根据需要返回(使用munmap(。您可以通过其第一个参数和可选的MAP_FIXED标志告诉mmap将页面映射到特定地址(当然,您必须确保该地址不会被占用(,这样您就可以建立一个连续内存区域。如果你提前分配整个内存,那么这不是问题,你可以用一个mmap来完成,并让操作系统选择一个方便的地方来映射它。最终,这就是malloc在内部所做的。对于没有sys/mman.h的平台,如果您能够接受这样一个事实,即在这些平台上,您不会提前返回内存,那么就不难重新使用malloc

我怀疑,如果您的分配大小总是页面大小的倍数,那么realloc将足够聪明,不会复制任何数据。不过,您必须尝试一下,看看它在特定的目标平台上是否有效(或者查阅malloc的文档(。

mremap可能就是您所需要的。只要你在移动整个页面,你就可以做一个超快速的realloc(实际上内核会帮你做的(。