C中的内存使用问题
请帮忙:) 操作系统: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;
}
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(5)
出于充分的理由,几乎没有内存分配器将块返回到操作系统
内存只能以页为单位从程序中删除,即使这样也不太可能被观察到。
如果需要,calloc(3) 和 malloc(3) 会与内核交互以获取内存。但是,很少有 free(3) 的实现将内存返回给内核1,它们只是将其添加到 calloc() 和 malloc() 稍后将参考的空闲列表中,以便重用该内存。释放的块。这种设计方法有充分的理由。
即使 free() 想要将内存返回给系统,它也需要至少一个连续的内存页才能让内核真正保护该区域,因此释放一小块只会导致保护更改(如果它是)页面中的最后小块。
操作原理
因此,malloc(3) 在需要时从内核获取内存,最终以离散页倍数为单位。这些页面根据程序的需要进行划分或合并。 malloc 和 free 合作维护一个目录。它们在可能的情况下合并相邻的空闲块,以便能够提供大块。目录可能涉及也可能不涉及使用已释放块中的内存来形成链表。 (替代方案是更共享内存和分页友好,并且它涉及专门为目录分配内存。)即使特殊和可选的调试代码被编译成,Malloc 和 free 也几乎没有能力强制访问各个块程序。
1.事实上,很少有 free() 实现尝试将内存返回给系统,这根本不是因为实现者偷懒。
与内核交互比简单地执行库代码要慢得多,而且好处就会很小。大多数程序都有稳态或不断增加的内存占用,因此花在分析堆寻找可返回内存上的时间将完全浪费。其他原因包括内部碎片使得页面对齐的块不太可能存在,并且返回块很可能会将块碎片分散到任一侧。最后,少数返回大量内存的程序很可能会绕过 malloc() 并简单地分配和释放页面。
For good reasons, virtually no memory allocator returns blocks to the OS
Memory can only be removed from your program in units of pages, and even that is unlikely to be observed.
calloc(3) and malloc(3) do interact with the kernel to get memory, if necessary. But very, very few implementations of free(3) ever return memory to the kernel1, they just add it to a free list that calloc() and malloc() will consult later in order to reuse the released blocks. There are good reasons for this design approach.
Even if a free() wanted to return memory to the system, it would need at least one contiguous memory page in order to get the kernel to actually protect the region, so releasing a small block would only lead to a protection change if it was the last small block in a page.
Theory of Operation
So malloc(3) gets memory from the kernel when it needs it, ultimately in units of discrete page multiples. These pages are divided or consolidated as the program requires. Malloc and free cooperate to maintain a directory. They coalesce adjacent free blocks when possible in order to be able to provide large blocks. The directory may or may not involve using the memory in freed blocks to form a linked list. (The alternative is a bit more shared-memory and paging-friendly, and it involves allocating memory specifically for the directory.) Malloc and free have little if any ability to enforce access to individual blocks even when special and optional debugging code is compiled into the program.
1. The fact that very few implementations of free() attempt to return memory to the system is not at all due to the implementors slacking off.
Interacting with the kernel is much slower than simply executing library code, and the benefit would be small. Most programs have a steady-state or increasing memory footprint, so the time spent analyzing the heap looking for returnable memory would be completely wasted. Other reasons include the fact that internal fragmentation makes page-aligned blocks unlikely to exist, and it's likely that returning a block would fragment blocks to either side. Finally, the few programs that do return large amounts of memory are likely to bypass malloc() and simply allocate and free pages anyway.
如果您试图确定您的程序是否存在内存泄漏,那么
top
并不是适合该工作的工具(valrind
才是)。top
显示操作系统看到的内存使用情况。即使您调用free
,也不能保证释放的内存会返回给操作系统。通常情况下,不会。尽管如此,内存确实变得“空闲”,因为您的进程可以将其用于后续分配。编辑如果您的
libc
支持它,您可以尝试使用M_TRIM_THRESHOLD
。即使您确实遵循此路径,也会很棘手(靠近堆顶部的单个已使用块将阻止其下方的所有可用内存被释放到操作系统)。If you're trying to establish whether your program has a memory leak, then
top
isn't the right tool for the job (valrind
is).top
shows memory usage as seen by the OS. Even if you callfree
, there is no guarantee that the freed memory would get returned to the OS. Typically, it wouldn't. Nonetheless, the memory does become "free" in the sense that your process can use it for subsequent allocations.edit If your
libc
supports it, you could try experimenting withM_TRIM_THRESHOLD
. Even if you do follow this path, it's going to be tricky (a single used block sitting close to the top of the heap would prevent all free memory below it from being released to the OS).通常 free() 不会将物理内存归还给操作系统,它们仍然映射在进程的虚拟内存中。如果你分配了一大块内存,libc可能会通过mmap()来分配它;那么如果你释放它,libc可能会通过munmap()将内存释放给操作系统,在这种情况下,top会显示你的内存使用量下降了。
因此,如果您不想显式向操作系统释放内存,可以使用 mmap()/munmap()。
Generally free() doesn't give back physical memory to OS, they are still mapped in your process's virtual memory. If you allocate a big chunk of memory, libc may allocate it by mmap(); then if you free it, libc may release the memory to OS by munmap(), in this case, top will show that your memory usage comes down.
So, if you want't to release memory to OS explicitly, you can use mmap()/munmap().
当您
free()
内存时,它会返回到标准 C 库的内存池,而不是返回到操作系统。在操作系统的视野中,正如你通过top
看到的那样,进程仍然在“使用”这块内存。在此过程中,C 库已占用内存,并且将来可能会从malloc()
返回相同的指针。我将从不同的开始对其进行更多解释:
在调用
malloc
期间,标准库实现可能会确定该进程没有从操作系统分配足够的内存。此时,库将进行系统调用,以从操作系统向进程接收更多内存(例如,sbrk()
或VirtualAlloc()
系统调用分别为 Unix 或 Windows)。当库向操作系统请求额外的内存后,它会将此内存添加到可从
malloc
返回的内存结构中。稍后对malloc
的调用将使用该内存,直至其耗尽。然后,该库向操作系统请求更多内存。当您
释放
内存时,库通常不会将内存返回给操作系统。造成这种情况的原因有很多。原因之一是库作者相信您将再次调用malloc
。如果您不再调用malloc
,您的程序可能很快就会结束。无论哪种情况,将内存归还给操作系统都没有太大的好处。库可能不将内存返回给操作系统的另一个原因是操作系统的内存是在大的连续范围内分配的。仅当整个连续范围不再使用时才能返回它。调用
malloc
和free
的模式可能不会清除整个使用范围。When you
free()
memory, it is returned to the standard C library's pool of memory, and not returned to the operating system. In the vision of the operating system, as you see it throughtop
, the process is still "using" this memory. Within the process, the C library has accounted for the memory and could return the same pointer frommalloc()
in the future.I will explain it some more with a different beginning:
During your calls to
malloc
, the standard library implementation may determine that the process does not have enough allocated memory from the operating system. At that time, the library will make a system call to receive more memory from the operating system to the process (for example,sbrk()
orVirtualAlloc()
system calls on Unix or Windows, respectively).After the library requests additional memory from the operating system, it adds this memory to its structure of memory available to return from
malloc
. Later calls tomalloc
will use this memory until it runs out. Then, the library asks the operating system for even more memory.When you
free
memory, the library usually does not return the memory to the operating system. There are many reasons for this. One reason is that the library author believed you will callmalloc
again. If you will not callmalloc
again, your program will probably end soon. Either case, there is not much advantage to return the memory to the operating system.Another reason that the library may not return the memory to the operating system is that the memory from operating system is allocated in large, contiguous ranges. It could only be returned when an entire contiguous range is no longer in use. The pattern of calling
malloc
andfree
may not clear the entire range of use.两个问题:
在
make_cache_db()
中,该行可能应该阅读
否则,您将仅分配一个
cache_db_s
节点。您在
make_cache_db()
中分配cache_db
的方式似乎有错误。看来你的意图是返回一个指向链表第一个元素的指针;但由于您在循环的每次迭代中都重新分配cache_db
,因此您最终将返回指向列表的 最后 元素的指针。如果您稍后使用
free_cache_db()
释放该列表,这将导致您泄漏内存。但目前,这个问题被上一个要点中描述的错误所掩盖,该错误导致您分配长度仅为 1 的列表。独立于这些错误,aix 提出的观点非常有效:运行时库不需要将所有
free()d
内存返回给操作系统。Two problems:
In
make_cache_db()
, the lineshould probably read
Otherwise, you'll only allocate a single
cache_db_s
node.The way you're assigning
cache_db
inmake_cache_db()
seems to be buggy. It seems that your intention is to return a pointer to the first element of the linked list; but because you're reassigningcache_db
in every iteration of the loop, you'll end up returning a pointer to the last element of the list.If you later free the list using
free_cache_db()
, this will cause you to leak memory. At the moment, though, this problem is masked by the bug described in the previous bullet point, which causes you to allocate lists of only length 1.Independent of these bugs, the point raised by aix is very valid: The runtime library need not return all
free()
d memory to the operating system.