题
请帮助:) OS: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?? %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;
}
解决方案
如果您想确定您的程序是否存在内存泄漏,那么 top
不是适合这项工作的工具(valrind
是)。
top
显示操作系统所看到的内存使用情况。即使你打电话 free
, ,不能保证释放的内存会返回给操作系统。通常情况下,不会。尽管如此,内存确实变得“空闲”,因为您的进程可以将其用于后续分配。
编辑 如果你的 libc
支持,你可以尝试一下 M_TRIM_THRESHOLD
. 。即使您确实遵循此路径,也会很棘手(靠近堆顶部的单个已使用块将阻止其下方的所有可用内存被释放到操作系统)。
其他提示
出于充分原因,几乎没有内存分配器返回OS 的块
内存只能以页面为单位从程序中删除,甚至不太可能被观察到。
Calloc(3)和Malloc(3)与内核进行交互以在必要时获取内存。但是非常非常少,非常少数的免费(3)返回内存到内核 1 ,他们只是将它添加到一个免费的列表,即calloc()和malloc()将在稍后再次咨询,以便重用发布块。这种设计方法有很好的理由。
即使是一个空闲()想要将内存返回到系统,它需要至少一个连续的内存页面,以便让内核实际保护该区域,因此释放小块只会导致保护变化如果是页面中的最后小块。
操作理论
所以Malloc(3)在需要它时从内核获取内存,最终以离散页面倍数为单位。随着程序所需的要求,这些页面被分割或合并。 Malloc和免费合作维护目录。它们在可能的情况下,它们可以在可能提供大块的情况下结合邻近的块。目录可以或可能不涉及使用释放块中的存储器来形成链接列表。 (替代方案有点相同的内存和寻呼友好,它涉及专门为目录分配内存。)Malloc和免费如果在编译特殊和可选的调试代码时,即使在编译特殊和可选的调试代码时也有任何强制访问单个块的能力该计划。
1。由于实现的实施者松弛,因此,由于实现的实施者,返回到系统的空闲的实现很少的事实。
与内核的交互速度要慢得多只需执行库代码,福利将很小。大多数程序具有稳态或增加的内存占用空间,因此分析了寻找可转换内存的堆的时间将完全浪费。其他原因包括内部碎片使页面对齐的块不太可能存在的事实,并且很可能返回块将碎片块转移到任一侧。最后,返回大量内存的少数节目可能会绕过Malloc()并简单地分配和免费页面。
一般free()不会将物理内存放回OS,它们仍映射在您的流程的虚拟内存中。如果分配大块内存,则可以通过mmap()分配它;然后,如果您释放它,Libc可以通过Munmap()将内存释放到OS,在这种情况下,顶部将显示您的内存使用率下降。
所以,如果你不想明确地将内存释放到操作系统,可以使用mmap()/ munmap()。
当你 free()
内存,它返回到标准C库的内存池,而不是返回到操作系统。在操作系统的愿景中,如你所见 top
, ,进程仍在“使用”这块内存。在进程中,C 库已经占用了内存,并且可以从以下位置返回相同的指针: malloc()
将来。
我将以不同的开头对其进行更多解释:
在您致电期间 malloc
, ,标准库实现可能会确定进程没有从操作系统分配足够的内存。那时,库将进行系统调用,以从操作系统向进程接收更多内存(例如, sbrk()
或者 VirtualAlloc()
分别是 Unix 或 Windows 上的系统调用)。
当库向操作系统请求额外的内存后,它会将这块内存添加到可用于返回的内存结构中。 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
节点。您分配的方式
cache_db
在make_cache_db()
似乎有越野车。看来你的意图是返回一个指向链表第一个元素的指针;但因为你要重新分配cache_db
在循环的每次迭代中,您最终都会返回一个指向 最后的 列表的元素。如果您稍后使用释放列表
free_cache_db()
, ,这会导致你泄漏内存。但目前,这个问题被上一个要点中描述的错误所掩盖,该错误导致您分配长度仅为 1 的列表。
与这些 bug 无关,aix 提出的观点是非常有效的:运行时库不需要返回全部 free()
d 操作系统的内存。