是否可以在POSIX系统上部分释放动态分配的内存
Is it possible to partially free dynamically-allocated memory on a POSIX system?
我有一个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_ANONYMOUS
或MAP_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(实际上内核会帮你做的(。
- 如果C++类在类方法中具有动态分配,但没有构造函数/析构函数或任何非静态成员,那么它仍然是POD类型吗
- 使用动态分配的数组会导致代码分析发出虚假的C6386缓冲区溢出警告
- 调用析构函数以释放动态分配的内存
- 释放动态分配的内存时是否需要执行此额外步骤
- 在不释放所有动态分配的资源的情况下结束程序是否有风险
- 为什么在 C++ 执行删除操作后仍可以访问释放的动态分配的内存
- 如何释放动态分配的向量
- 如果我将内存动态分配给静态变量,我应该释放它还是会自动释放它
- 释放动态分配的内存时程序崩溃
- 当类对象完成时,是否为 C++ 类的数据成员动态分配的内存会释放
- 释放方法 C/C++ 中动态分配的 char*
- 释放 C++11 中动态分配的uv_timer_t (libuv) 实例
- 动态分配的阵列,双释放或损坏
- 在QT中释放动态分配的内存
- 动态分配的数组没有被释放
- 释放类的实例是否也会释放由其对象/方法动态分配的内存?
- 为类的成员变量释放动态分配的内存会产生错误
- 是否可以在POSIX系统上部分释放动态分配的内存
- 为什么valgrind显示泄漏,即使包含动态分配对象的向量被释放
- 当使用Ctrl-C中断程序时,如何释放动态分配的空间