请帮忙:)操作系统 : Linux
在"sleep(1000);"中,此时"top(显示Linux任务)"写了我7.7%MEM使用。瓦尔格林德:未发现内存泄漏。
我理解,写得正确,所有 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
~ #top
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?? %sn",*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?? %sn",cache_db==NULL?"yes":"no");
free_cache_db(&cache_db);
printf("For %d n", ii);
}
printf("END : Is it free?? %sn",cache_db==NULL?"yes":"no");
printf("END n");
sleep(1000);
return 0;
}
出于充分的理由,几乎没有内存分配器将块返回给操作系统
内存只能以页为单位从程序中删除,甚至不太可能观察到。
如有必要,calloc(3) 和 malloc(3) 会与内核交互以获取内存。但是很少有 free(3) 的实现会把内存返回到内核1 中,他们只是将内存添加到一个自由列表中,calloc() 和 malloc() 稍后会参考该列表以重用释放的块。这种设计方法有充分的理由。
即使 free() 想要将内存返回给系统,它也需要至少一个连续的内存页才能让内核真正保护该区域,因此释放一个小块只会导致保护更改,如果它是页面中的最后一个小块。
操作理论
所以 malloc(3) 在内核需要的时候从内核获取内存,最终以离散页倍数为单位。这些页面根据程序需要进行拆分或合并。Malloc 和自由合作维护一个目录。它们在可能的情况下合并相邻的自由块,以便能够提供大块。该目录可能涉及也可能不涉及使用释放块中的内存来形成链表。(另一种方法是对共享内存和分页更加友好,它涉及专门为目录分配内存。Malloc 和 free 几乎没有能力强制访问单个块,即使将特殊和可选的调试代码编译到程序中也是如此。
1. 很少有 free() 的实现试图将内存返回系统这一事实根本不是由于实现者懈怠。
与内核交互比简单地执行库代码要慢得多,而且好处很小。大多数程序具有稳定状态或不断增加的内存占用,因此分析堆以查找可返回内存所花费的时间将完全浪费。其他原因包括内部碎片使页面对齐的块不太可能存在,并且返回块可能会将块碎片化到任何一侧。最后,少数返回大量内存的程序可能会绕过 malloc() 并简单地分配和释放页面。
如果你试图确定你的程序是否有内存泄漏,那么top
不是适合这项工作的工具(valrind
是)。
top
显示操作系统看到的内存使用情况。即使您调用free
,也不能保证释放的内存会返回到操作系统。尽管如此,内存确实变得"空闲",因为您的进程可以将其用于后续分配。
编辑 如果您的libc
支持它,您可以尝试尝试使用 M_TRIM_THRESHOLD
。即使你遵循这条路径,也会很棘手(靠近堆顶部的单个使用块会阻止其下的所有可用内存发布到操作系统)。
通常 free() 不会将物理内存返回给操作系统,它们仍然映射到进程的虚拟内存中。如果你分配了一大块内存,libc 可能会通过 mmap() 分配它;然后,如果您释放它,libc 可能会通过 munmap() 将内存释放到操作系统,在这种情况下,top 将显示您的内存使用量下降。
所以,如果你不想明确地向操作系统释放内存,你可以使用 mmap()/munmap()。
当您free()
内存时,它会返回到标准 C 库的内存池中,而不是返回到操作系统。 在操作系统的愿景中,正如您所看到的 top
,该过程仍在"使用"此内存。 在此过程中,C 库已占用内存,将来可能会从malloc()
返回相同的指针。
我将用不同的开头再解释一下:
在调用 malloc
期间,标准库实现可能会确定进程没有足够的操作系统分配内存。 此时,库将进行系统调用,以接收从操作系统到进程的更多内存(例如,分别在 Unix 或 Windows 上sbrk()
或VirtualAlloc()
系统调用)。
库从操作系统请求额外的内存后,它会将此内存添加到其可从malloc
返回的内存结构中。 以后对malloc
的调用将使用此内存,直到它用完为止。 然后,库要求操作系统提供更多内存。
free
内存时,库通常不会将内存返回到操作系统。 造成这种情况的原因有很多。 一个原因是图书馆作者相信你会再次打电话给malloc
。 如果您不再打电话给malloc
,您的程序可能很快就会结束。 无论哪种情况,将内存返回到操作系统都没有太大优势。
库可能不会将内存返回到操作系统的另一个原因是,操作系统中的内存是在较大的连续范围内分配的。 仅当整个连续范围不再使用时,才能返回它。 调用malloc
和free
的模式可能无法清除整个使用范围。
两个问题:
-
在
make_cache_db()
中,这条线for (int i=0; i = n; i++)
应该读
for (int i=0; i<n; i++)
否则,您将只分配一个
cache_db_s
节点。 -
您在
make_cache_db()
中分配cache_db
的方式似乎有问题。似乎您的意图是返回指向链表第一个元素的指针;但是,由于您在循环的每次迭代中重新分配cache_db
,因此最终将返回指向列表最后一个元素的指针。如果您稍后使用
free_cache_db()
释放列表,这将导致您泄漏内存。但是,目前,此问题被上一个项目符号中描述的错误所掩盖,该错误导致您分配长度仅为 1 的列表。
除了这些错误之外,aix 提出的观点非常有效:运行时库不需要将所有free()
内存返回给操作系统。