0

请帮助:) 操作系统:Linux

在“sleep(1000);”中,此时“top(显示 Linux 任务)”给我写了 7.7 %MEM 使用。valgrind:未发现内存泄漏。

我明白,写得正确,所有 malloc 结果都是 NULL。但是为什么在这个时候“睡眠”我的程序没有减少内存?缺少什么?

对不起我的英语不好,谢谢


~ # tmp_soft
For : Is it free??  no
Is it free??  yes
For 0 
For : Is it free??  no
Is it free??  yes
For 1 
END : Is it free??  yes
END 

〜#顶部
  PID USER      PR  NI  VIRT  RES  SHR S %CPU %MEM    TIME+  COMMAND                                                                
23060 root      20   0  155m 153m  448 S    0  7.7   0:01.07 tmp_soft    

完整来源:tmp_soft.c

#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <unistd.h>

struct cache_db_s
{
 int       table_update;
 struct    cache_db_s * p_next;
};

void free_cache_db (struct cache_db_s ** cache_db)
{
 struct cache_db_s * cache_db_t;
 while (*cache_db != NULL)
 {
  cache_db_t = *cache_db;
  *cache_db = (*cache_db)->p_next;
  free(cache_db_t);
  cache_db_t = NULL;
 }
 printf("Is it free??  %s\n",*cache_db==NULL?"yes":"no");
}

void make_cache_db (struct cache_db_s ** cache_db)
{
 struct cache_db_s * cache_db_t = NULL;
 int n = 10000000;

 for (int i=0; i = n; i++)
 {
  if ((cache_db_t=malloc(sizeof(struct cache_db_s)))==NULL) {
   printf("Error : malloc 1 -> cache_db_s (no free memory) \n");
   break;
  }
  memset(cache_db_t, 0, sizeof(struct cache_db_s));

  cache_db_t->table_update = 1; // tmp 

  cache_db_t->p_next = *cache_db;
  *cache_db = cache_db_t;
  cache_db_t = NULL;
 }
}

int main(int argc, char **argv)
{
 struct cache_db_s * cache_db = NULL;

 for (int ii=0; ii  2; ii++) {
  make_cache_db(&cache_db);
  printf("For : Is it free??  %s\n",cache_db==NULL?"yes":"no");
  free_cache_db(&cache_db);
  printf("For %d \n", ii);
 }

 printf("END : Is it free??  %s\n",cache_db==NULL?"yes":"no");
 printf("END \n");
 sleep(1000);
 return 0;
}
4

5 回答 5

11

出于充分的理由,几乎没有内存分配器将块返回给操作系统


内存只能以页面为单位从程序中删除,即使这样也不太可能被观察到。

如有必要,calloc(3) 和 malloc(3) 会与内核交互以获取内存。但是很少有 free(3) 的实现将内存返回给内核1,他们只是将它添加到一个空闲列表中,稍后 calloc() 和 malloc() 将查询以重用已释放的块。这种设计方法有充分的理由。

即使 free() 想要将内存返回给系统,它也需要至少一个连续的内存页面才能让内核真正保护该区域,因此释放一个小块只会导致保护更改,如果它是页面中的最后一个小块。

操作理论

因此 malloc(3) 在需要时从内核获取内存,最终以离散页面倍数为单位。这些页面根据程序需要进行划分或合并。malloc 和 free 合作维护一个目录。它们尽可能合并相邻的空闲块,以便能够提供大块。该目录可能涉及也可能不涉及使用已释放块中的内存来形成链表。(替代方案更共享内存和分页友好,它涉及专门为目录分配内存。)即使特殊和可选的调试代码被编译成,Malloc 和 free 也几乎没有强制访问单个块的能力。该程序。


1. free() 的实现很少尝试将内存返回给系统,这根本不是因为实现者的懈怠。

与内核交互比简单地执行库代码要慢得多,而且好处很小。大多数程序都有一个稳定状态或不断增加的内存占用,因此花在分析堆以寻找可返回内存的时间将被完全浪费掉。其他原因包括内部碎片使得页面对齐的块不太可能存在,并且返回块很可能会将块碎片到任一侧。最后,少数确实返回大量内存的程序可能会绕过 malloc() 并简单地分配和释放页面。

于 2011-05-13T16:29:05.547 回答
4

如果您试图确定您的程序是否存在内存泄漏,那么top它不是适合该工作的工具(valrindis)。

top显示操作系统看到的内存使用情况。即使您调用free,也不能保证释放的内存会返回给操作系统。通常情况下,它不会。尽管如此,内存确实变得“空闲”,因为您的进程可以将其用于后续分配。

编辑如果您libc支持它,您可以尝试使用M_TRIM_THRESHOLD. 即使您确实遵循这条路径,它也会很棘手(靠近堆顶部的单个使用块会阻止其下方的所有空闲内存被释放到操作系统)。

于 2011-05-13T15:59:56.697 回答
2

通常 free() 不会将物理内存归还给操作系统,它们仍然映射在进程的虚拟内存中。如果你分配了一大块内存,libc 可能会通过 mmap(); 那么如果你释放它,libc 可能会通过 munmap() 将内存释放给操作系统,在这种情况下,top 将显示你的内存使用量下降了。

所以,如果你不想显式地向操作系统释放内存,你可以使用 mmap()/munmap()。

于 2011-05-13T16:27:13.727 回答
1

当你free()内存时,它会返回到标准 C 库的内存池,而不是返回到操作系统。在操作系统的视野中,正如你所看到的那样top,进程仍在“使用”这块内存。在这个过程中,C 库已经占用了内存,并且将来可能会返回相同的指针malloc()

我将用不同的开头再解释一下:

在您调用 期间malloc,标准库实现可能会确定该进程没有从操作系统分配的足够内存。那时,库将进行系统调用以从操作系统接收更多内存给进程(例如,分别在 Unix 或 Windows 上进行系统调用)sbrk()VirtualAlloc()

在库向操作系统请求额外的内存后,它会将这些内存添加到它的内存结构中,以便从中返回malloc。稍后调用malloc将使用此内存,直到它用完。然后,库向操作系统请求更多内存。

当您free进行内存时,库通常不会将内存返回给操作系统。这件事情是由很多原因导致的。一个原因是图书馆作者相信你会malloc再次致电。如果您不再打电话malloc,您的程序可能很快就会结束。无论哪种情况,将内存返回给操作系统都没有太大的优势。

库可能不会将内存返回给操作系统的另一个原因是来自操作系统的内存分配在大的、连续的范围内。只有当整个连续范围不再使用时才能返回。调用的模式mallocfree可能无法明确整个使用范围。

于 2011-05-13T16:04:15.737 回答
0

两个问题:

  • make_cache_db(), 线

    for (int i=0; i = n; i++)
    

    应该读

    for (int i=0; i<n; i++)
    

    否则,您将只分配一个cache_db_s节点。

  • 您分配的方式cache_db似乎make_cache_db()有问题。看来您的意图是返回指向链表第一个元素的指针;但是因为你cache_db在循环的每次迭代中都重新分配,你最终会返回一个指向列表最后一个元素的指针。

    如果您稍后使用 释放列表free_cache_db(),这将导致您泄漏内存。不过,目前,这个问题被上一个要点中描述的错误所掩盖,这导致您分配的列表长度仅为 1。

抛开这些 bug,aix 提出的观点非常有效:运行时库不需要将所有free()d 内存返回给操作系统。

于 2011-05-13T16:05:17.227 回答