如何保证在释放内存时,操作系统将回收该内存以供其使用?

时间:2018-01-20 16:09:15

标签: c linux memory memory-management

我注意到这个程序:

#include <stdio.h>

int main() {
  const size_t alloc_size = 1*1024*1024;
  for (size_t i = 0; i < 3; i++) {
    printf("1\n");
    usleep(1000*1000);
    void *p[3];
    for (size_t j = 3; j--; )
      memset(p[j] = malloc(alloc_size),0,alloc_size); // memset for de-virtualize the memory
    usleep(1000*1000);
    printf("2\n");
    free(p[i]);
    p[i] = NULL;
    usleep(1000*1000*4);
    printf("3\n");
    for (size_t j = 3; j--; )
      free(p[j]);
  }
}

分配3个存储器,3次并且每次释放不同的存储器,根据watch free -m释放存储器,这意味着OS回收了每个free的存储器,而不管存储器是什么。在程序的地址空间内的位置。我可以以某种方式得到这种效果的保证吗?或者是否有类似的东西(如>64KB分配规则)?

4 个答案:

答案 0 :(得分:5)

简短的回答是:一般情况下,您不能保证操作系统会回收释放的内存,但可能有特定于操作系统的方法来执行此操作或更好的方法来确保此类行为。

答案很长:

  • 您的代码具有未定义的行为:free(p[i]);printf("2\n");访问超出p数组末尾的额外mmap

  • 您为库进行单独的系统调用(例如Linux系统中的free)分配大块(1 MB),malloc()将这些块释放到操作系统,因此观察到的行为。

  • 各种操作系统可能会针对系​​统特定阈值(通常为128KB)实现此类行为,但C标准对此提供了保证,因此依赖此类行为是系统特定的。

  • 阅读系统上MMAP_THRESHOLD的手册页,了解是否可以控制此行为。例如,Linux上的C库使用环境变量mmap()来覆盖此阈值的默认设置。

  • 如果您编程为Posix目标,您可能希望直接使用malloc而不是munmap()来保证在使用mmap()解除分配后将内存返回给系统。请注意,{ name: "JB", age: 35, children: [ { name: "Alice", age: "5", favColor: "pink" }, { name: "Bob", age: "8", favColor: "blue" }, { name: "Charlie", age: "9", favColor: "green" }, ] } 返回的块在第一次访问之前将初始化为所有位零,因此您可以避免这种显式初始化以利用按需分页,执行显式初始化以确保内存映射到尝试在以后的操作中尽量减少延迟。

答案 1 :(得分:1)

在我认识的操作系统上,特别是在linux上:

不,你不能保证重复使用。你为什么要那样?只有当有人需要更多内存页面时才会重复使用,然后Linux必须选择当前未映射到进程的页面;如果这些用完了,你就会进行交换。而且:你不能让你的操作系统做一些与你的流程业务无关的事情。内部管理内存分配的方式不是解放过程的业务。事实上,这在安全方面是一件好事。

你可以做的不仅是释放内存(可能会将其分配给你的进程,由你的libc处理,以供后来的mallocs使用),但实际上是将它释放回来(man munmapfree , 玩得开心)。这不是你经常做的事情。

另外:这是另一个“帮助,linux吃了我的RAM”的实例化......你误解了 var num1 = document.querySelector('.num1').innerHTML; var num2 = document.querySelector('.num2').innerHTML; num1.addEventListener('click',function num() { console.log('working'); }) <button class="num1">1</button> <button class="num2">2</button> 告诉你的内容。

答案 2 :(得分:1)

对于glibc malloc(),请阅读man 3 malloc手册页。

简而言之,较小的分配使用sbrk()提供的内存来扩展数据段;这不会返回给操作系统。更大的分配(通常为132 KiB或更高;您可以在glibc上使用MMAP_THRESHOLD来更改限制)使用mmap()来分配匿名内存页面(但也包括这些页面上的内存分配簿记),并在释放时,这些通常会立即返回操作系统。

唯一的情况是,如果您有一个长时间运行的进程,暂时执行非常大的分配,在嵌入式或其他方面运行,那么您应该担心进程及时将内存返回到操作系统的唯一情况内存受限设备。为什么?因为这些东西已经在C中成功完成了几十年,并且C库和OS内核确实处理了这些情况。在正常情况下,这不是一个实际问题。如果你知道这是一个实际问题,你只需要担心它;除非在非常具体的情况下,否则它不会成为实际问题。

我个人经常在Linux中使用mmap(2)来映射大量数据集的页面。在这里,“巨大”意味着“太大而无法容纳RAM和交换”。

最常见的情况是我拥有一个真正庞大的二进制数据集。然后,我创建一个合适大小的(稀疏)后备文件,以及该文件的内存映射。几年前,在另一个论坛中,我展示了如何使用terabyte data set - 是的,1,099,511,627,776字节 - 其中只有250兆字节左右在该示例中实际操作,以保留数据文件的示例小。这种方法的关键是使用MAP_SHARED | MAP_NORESERVE来确保内核不为此数据集使用交换内存(因为它不够,并且失败),但直接使用文件支持。我们可以使用madvise()来通知内核我们可能的访问模式作为优化,但在大多数情况下它没有那么大的影响(因为内核启发式无论如何都做得很好)。我们还可以使用msync()来确保将某些部分写入存储。 (某些特效会影响到。其他进程会读取支持映射的文件,特别是取决于它们是否正常读取,或使用O_DIRECT之类的选项;如果通过NFS或类似方式共享,那么wrt。进程读取远程文件。这一切都非常复杂。)

如果您决定使用mmap()获取匿名内存页,请注意您需要跟踪指针和长度(长度是页面大小的倍数,sysconf(_SC_PAGESIZE)) ,以便您稍后可以使用munmap()释放映射。显然,这与正常的内存分配完全分开(malloc()calloc()free());但除非你试图使用特定地址,否则两者不会相互干扰。

答案 3 :(得分:0)

如果您希望操作系统回收内存,则需要使用操作系统服务来分配内存(在页面中分配)。取消分配内存,您需要调用从您的进程中删除页面的操作系统服务。

除非您编写自己的malloc / free,否则您将无法使用现成的库函数来实现目标。