请帮忙:) 操作系统: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
~ #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?? %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;
}
答案 0 :(得分:10)
只能以页面为单位从程序中删除内存,即使这样也不太可能被观察到。
如果需要,calloc(3)和malloc(3)与内核进行交互以获取内存。但很少有free(3)实现将内存返回到内核 1 ,他们只是将它添加到一个空闲列表中,calloc()和malloc()稍后会参考以便重用释放块。这种设计方法有充分的理由。
即使free()想要将内存返回给系统,它也需要至少一个连续的内存页才能让内核真正保护该区域,因此释放一个小块只会导致保护更改如果它是页面中的 last 小块。
因此malloc(3)在需要时从内核获取内存,最终以离散页面倍数为单位。这些页面按程序要求进行划分或合并。 Malloc和免费合作维护目录。它们在可能的情况下合并相邻的自由块,以便能够提供大块。该目录可能涉及或可能不涉及使用释放块中的存储器来形成链表。 (替代方案是更多共享内存和分页友好,它涉及专门为目录分配内存。)即使特殊和可选的调试代码被编译成,Malloc和free也几乎没有能力强制访问各个块。该计划。
1。事实上,很少有free()实现尝试将内存返回系统,这完全不是由于实现者的松弛。
与内核交互要慢得多只需执行库代码,效益就会很小。大多数程序具有稳态或增加的内存占用,因此分析堆寻找可返回内存所花费的时间将被完全浪费。其他原因包括内部碎片使页面对齐的块不太可能存在,并且返回块可能会将块分块到任何一方。最后,少数几个返回大量内存的程序可能会绕过malloc()并简单地分配和释放页面。
答案 1 :(得分:4)
如果您正在尝试确定您的程序是否存在内存泄漏,那么top
不适合该作业(valrind
)。
top
显示操作系统看到的内存使用情况。即使您调用free
,也无法保证释放的内存将返回到操作系统。通常,它不会。尽管如此,在您的进程可以将其用于后续分配的意义上,内存确实变得“免费”。
修改如果libc
支持,您可以尝试使用M_TRIM_THRESHOLD
进行试验。即使你确实遵循这条路径,它也会变得棘手(靠近堆顶部的一个使用过的块会阻止它下面的所有空闲内存被释放到操作系统)。
答案 2 :(得分:2)
通常free()不会向OS返回物理内存,它们仍会映射到进程的虚拟内存中。如果你分配了一大块内存,libc可以通过mmap()分配它;然后,如果你释放它,libc可能会通过munmap()将内存释放到操作系统,在这种情况下,top会显示你的内存使用率下降。
因此,如果您不想显式地向OS释放内存,可以使用mmap()/ munmap()。
答案 3 :(得分:1)
当你free()
内存时,它会返回到标准C库的内存池,而不会返回给操作系统。在操作系统的愿景中,正如您通过top
看到的那样,该过程仍在“使用”此内存。在这个过程中,C库占用了内存,并且可以在将来从malloc()
返回相同的指针。
我会用不同的开头解释一下:
在调用malloc
期间,标准库实现可能会确定进程没有足够的操作系统分配的内存。那时,库将进行系统调用以从操作系统接收更多内存到进程(例如,分别在Unix或Windows上进行sbrk()
或VirtualAlloc()
系统调用。)
在库从操作系统请求额外内存后,它会将此内存添加到可从malloc
返回的内存结构中。稍后调用malloc
将使用此内存,直到它用完为止。然后,库会要求操作系统提供更多内存。
当你free
内存时,库通常不会将内存返回给操作系统。这件事情是由很多原因导致的。一个原因是图书馆作者认为你会再次致电malloc
。如果您不再次致电malloc
,您的计划可能会很快结束。无论哪种情况,将内存返回给操作系统都没有太大的优势。
库可能无法将内存返回给操作系统的另一个原因是来自操作系统的内存是在大的连续范围内分配的。它只能在不再使用整个连续范围时返回。调用malloc
和free
的模式可能无法清除整个使用范围。
答案 4 :(得分: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
,你最终会返回一个指向列表的 last 元素的指针。
如果您稍后使用free_cache_db()
释放列表,则会导致内存泄漏。但是,此时此问题被前一个项目符号中描述的错误所掩盖,导致您只分配长度为1的列表。
独立于这些错误,aix提出的观点非常有效:运行时库不需要将所有free()
内存返回给操作系统。