【问题标题】:Returning dynamically allocated memory back to OS without terminating the program在不终止程序的情况下将动态分配的内存返回给操作系统
【发布时间】:2014-04-14 21:44:57
【问题描述】:

我正在开发一个使用大量但有限内存的程序。内存在不同线程的运行时分配和释放。但是,我注意到程序的内存使用量不会保持在指定范围内。随着时间的推移,它会增加。我编写了以下示例程序来检查内存是否被释放回操作系统。已释放一半分配的内存以检查内存使用情况是否下降。

int main()
{
    char *p[COUNT];

    for(int i = 0; i < COUNT; i++)
    {
        p[i] = new char[1048576];
        memset (p[i], 0, 1048576);
        printf("%p\n", p[i]);
    }

    printf("done allocating ... \n");

    sleep(10);

    printf("Freeing\n");
    for(int i; i < COUNT; i++)
    {
        delete[] p[i];
    }

    while(1)
        sleep(1);
}

运行程序后,操作系统似乎不会回收释放的页面。在分配和释放之后,内存使用情况与 linux 中的“top”命令中看到的相同。它只是将页面标记为免费,以供同一程序重用。 在我的程序中,malloc 和 free 运行在不同的线程上。当 malloc 比 free 更频繁地调用并且进程数据段增长非常大时,这会导致内存管理问题,导致操作系统将页面交换到磁盘。这使程序和操作系统变得缓慢且无响应。操作系统有没有办法回收释放的内存?

【问题讨论】:

  • 分配 2000 与解除分配 1000 ???
  • 你错了。保留虚拟内存不会使程序或操作系统变慢且无响应。操作系统确实回收释放的物理内存。回收虚拟内存毫无意义,因为它不是稀缺资源。
  • @Dieter Lucking,只释放一半分配的内存,看看内存使用率是否下降
  • “但这次释放所有内存”是什么意思?根本不清楚你在做什么。 (另外,在第二个 for 循环之前,您的代码永远不会将 i 设置回 0。)
  • 你肯定有内存管理问题——那个程序根本不会释放任何内存。

标签: c++ linux memory-management malloc free


【解决方案1】:

在 Linux 上,您可能会使用 mmap(2)(由 malloc::operator new 等使用)获得一些空间(在 virtual memory 中)。然后你可以稍后发布它munmap

由于mmapmunmap 有点贵,malloc(因此::operator new 通常在malloc 之上实现)尝试重用以前的free-d 内存区域,所以不要费心总是向内核释放内存(低于一个很大的阈值,可能是 128K 或更多)。

顺便说一句,proc(5) 为内核提供了一个有用的接口来查询事物。对于 pid 1234 的进程,您可以cat /proc/1234/maps 显示其地址空间内存映射(在您的进程内部,使用/proc/self/maps

所以你可以编码:

const size_t sz = 1048576;
/// allocating loop
for (i=0; i < 2000 ;i++ ) {
  void* ad = mmap(NULL, sz, PROT_READ|PROT_WRITE, 
                  MMAP_PRIVATE|MMAP_ANONYMOUS,
                  -1, (off_t)0);
  if (ad == MMAP_FAILED)
    { perror("mmap"); exit (EXIT_FAILURE); }
  p[i] = ad;
  memset (p[i], 0, sz); // actually uneeded
}
printf ("done allocating ... \n");
sleep(5);
// freeing loop
printf("Freeing\n");
for (i=0; i < 2000 ;i++ ) {
  if (munmap((void*)p[i], sz)) 
    { perror("munmap"); exit(EXIT_FAILURE); }
  p[i] = nullptr;
}

请注意,mmapMAP_ANONYMOUS 在成功时会提供一个归零的内存区域,因此您不需要 memset 来清除它。

在 C++ 中,您还可以定义自己的 operator new(调用 mmap)和 operator delete (打电话给munmap)如果需要的话。

另请阅读Advanced Linux Programming

【讨论】:

    【解决方案2】:

    进程退出后,进程使用的所有内存都将被回收。操作系统可能会以推测方式缓存数据以防万一(如Linux Ate My RAM 中所述)。当另一个进程需要它时,内存将被释放。

    编辑:因为你的意思是一个长时间运行的服务器进程,那么你关心的是进程仍在运行时的内存使用情况。我可以建议valgrind 作为检测应用程序内存泄漏的工具,并建议RAII 作为防止内存泄漏的编码技术。请注意,Linux 上的内存使用情况(以及一般情况下!)难以测量/解释,ps 和 top 等工具的输出可能会产生误导和不直观,例如根据this answer

    【讨论】:

    • 我同意,但我的进程是服务器进程,永远不应该退出
    • @user3296247:那么my answer很有用。
    • @BasileStarynkevitch 当然是,但没有足够的声誉:P
    • @user3296247:如果合适的话,你可以接受——即使声誉不高——一些答案(例如我的)。
    猜你喜欢
    • 2015-10-12
    • 2018-01-14
    • 2011-10-07
    • 2014-11-06
    • 1970-01-01
    • 2012-04-05
    • 2015-12-31
    • 1970-01-01
    相关资源
    最近更新 更多