我知道如何使用malloc()
和free()
来分配内存,但是也有一个标准的C函数来检查剩下多少内存,所以我可以定期调用它来确保我的代码没有内存泄漏?
我唯一能想到的是调用malloc(1)
在一个无限循环,直到它返回一个错误,但不应该有一个更有效的方式?
Linux glibc sysconf(_SC_AVPHYS_PAGES)
和get_avphys_pages()
这两个glibc扩展应该为您提供可用的页面数。然后,我们只需将其乘以页面大小sysconf(_SC_PAGESIZE)
就可以找到以字节为单位的总可用内存。
c
#define _GNU_SOURCE
#include <stdio.h>
#include <sys/sysinfo.h>
#include <unistd.h>
int main(void) {
/* PAGESIZE is POSIX: http://pubs.opengroup.org/onlinepubs/9699919799/
* but PHYS_PAGES and AVPHYS_PAGES are glibc extensions. I bet those are
* parsed from /proc/meminfo. */
printf(
"sysconf(_SC_PHYS_PAGES) * sysconf(_SC_PAGESIZE) = 0x%lXn",
sysconf(_SC_PHYS_PAGES) * sysconf(_SC_PAGESIZE)
);
printf(
"sysconf(_SC_AVPHYS_PAGES) * sysconf(_SC_PAGESIZE) = 0x%lXn",
sysconf(_SC_AVPHYS_PAGES) * sysconf(_SC_PAGESIZE)
);
/* glibc extensions. man says they are parsed from /proc/meminfo. */
printf(
"get_phys_pages() * sysconf(_SC_PAGESIZE) = 0x%lXn",
get_phys_pages() * sysconf(_SC_PAGESIZE)
);
printf(
"get_avphys_pages() * sysconf(_SC_PAGESIZE) = 0x%lXn",
get_avphys_pages() * sysconf(_SC_PAGESIZE)
);
}
GitHub上游。
编译并运行:
gcc -ggdb3 -O0 -std=c99 -Wall -Wextra -pedantic -o main.out main.c
./main.out
示例输出在我的32gb RAM系统上:
sysconf(_SC_PHYS_PAGES) * sysconf(_SC_PAGESIZE) = 0x7CCFFC000
sysconf(_SC_AVPHYS_PAGES) * sysconf(_SC_PAGESIZE) = 0x6383FD000
get_phys_pages() * sysconf(_SC_PAGESIZE) = 0x7CCFFC000
get_avphys_pages() * sysconf(_SC_PAGESIZE) = 0x6383FD000
0x7CCFFC000比32GiB小一点,是总RAM。0x6383FD000是可用的。
man get_avphys_pages
说它从/proc/meminfo
获取数据。
不,没有标准的C函数可以做到这一点。您可以使用一些特定于平台的函数来执行某些类型的查询(如工作集大小),但这些函数可能不会有帮助,因为有时已经正确地free()
d的内存仍然被认为是由操作系统分配的,因为malloc
实现可能会将释放的内存保留在池中。
如果你想检查内存泄漏,我强烈建议使用像Valgrind这样的工具,它可以在虚拟机中运行你的程序,并且可以跟踪内存泄漏,以及其他功能。
如果你在Windows上运行,你可以使用_CrtDbgReport
和/或_CrtSetDbgFlag
来检查内存泄漏
如果在您的系统中malloc()
总是分配物理内存,那么您可以重复调用malloc()
,其大小的差别不是1,而是连续的2的幂。这样会更有效率。下面是如何做到这一点的一个例子。
另一方面,如果malloc()
只分配虚拟地址空间,而不映射物理内存到它,这不会给你想要的。
示例代码:
#include <stdio.h>
#include <stdlib.h>
void* AllocateLargestFreeBlock(size_t* Size)
{
size_t s0, s1;
void* p;
s0 = ~(size_t)0 ^ (~(size_t)0 >> 1);
while (s0 && (p = malloc(s0)) == NULL)
s0 >>= 1;
if (p)
free(p);
s1 = s0 >> 1;
while (s1)
{
if ((p = malloc(s0 + s1)) != NULL)
{
s0 += s1;
free(p);
}
s1 >>= 1;
}
while (s0 && (p = malloc(s0)) == NULL)
s0 ^= s0 & -s0;
*Size = s0;
return p;
}
size_t GetFreeSize(void)
{
size_t total = 0;
void* pFirst = NULL;
void* pLast = NULL;
for (;;)
{
size_t largest;
void* p = AllocateLargestFreeBlock(&largest);
if (largest < sizeof(void*))
{
if (p != NULL)
free(p);
break;
}
*(void**)p = NULL;
total += largest;
if (pFirst == NULL)
pFirst = p;
if (pLast != NULL)
*(void**)pLast = p;
pLast = p;
}
while (pFirst != NULL)
{
void* p = *(void**)pFirst;
free(pFirst);
pFirst = p;
}
return total;
}
int main(void)
{
printf("Total free: %zun", GetFreeSize());
printf("Total free: %zun", GetFreeSize());
printf("Total free: %zun", GetFreeSize());
printf("Total free: %zun", GetFreeSize());
printf("Total free: %zun", GetFreeSize());
return 0;
}
输出(ideone):
Total free: 266677120
Total free: 266673024
Total free: 266673024
Total free: 266673024
Total free: 266673024
如果你能负担得起#ifdef调试版本(可能在模拟器中!),你可以构建一个malloc/free的调试版本,跟踪当前使用的字节数,并定期"打印"它(再次-仅在调试版本中,可能在模拟器下)在任何输出设备上调试(led?),看看它是否不断增加。
标准的技巧是分配sizeof(size_t)多于请求,从而将大小与新分配的内存存储在一起-但是如果你正在编写固件,我猜你已经知道了:)
所以…你有模拟器吗?
编辑:我已经习惯了运行在GHz的计算机,所以我一开始并没有想到这一点,但是当然你可以做的另一件事就是计算分配的数量,而不是它们的大小——我无法想象这怎么可能占用太多的内存来运行。
我搜索并发现了这个问题,以帮助我制作一个应用程序,在多个复杂值数组上动画分形函数的多次迭代。
谢谢Alexey Frunze的ideone.c代码。这确实很有帮助。基于我所学到的,希望能提供更多帮助,我写下了以下内容:
/* File: count-free-blocks.c
*
* Revision: 2018-24-12
*
* Copyright (C) Randall Sawyer
* <https://stackoverflow.com/users/341214/randall-sawyer>
*/
#include <stdarg.h>
#include <stdint.h>
#include <stdio.h>
#include <stdlib.h>
size_t available_blocks (size_t block_sz);
size_t largest_n_blocks (size_t block_sz);
size_t try_alloc (size_t n_blocks,
size_t block_sz);
void report (int indent,
const char *format,
...);
int main (int argc, const char **argv)
{
size_t n_blocks,
block_sz = 0;
if (argc > 1 && sscanf (argv[1], "%zu", &block_sz) != 1)
report (0, "Argument `%s' is not a valid block size.", argv[1]);
if (block_sz == 0)
{
report (0, "Using 1 byte block size...");
block_sz = 1;
}
n_blocks = available_blocks (block_sz);
report (0, "Available memory: %zu blocks of %zu bytes == %zu bytes",
n_blocks, block_sz, n_blocks * block_sz);
return 0;
}
size_t
available_blocks (size_t block_sz)
{
size_t n_blocks[2];
report (0, "calculating free memory...");
/* Calculate maximum number of blocks of given size which can be
* repeatedly allocated.
*/
do {
for ( n_blocks[0] = largest_n_blocks (block_sz);
(n_blocks[1] = largest_n_blocks (block_sz)) < n_blocks[0];
n_blocks[0] = n_blocks[1] );
report (1, "check once more...");
} while (try_alloc (n_blocks[0], block_sz) != n_blocks[0]);
return n_blocks[0];
}
size_t
largest_n_blocks (size_t block_sz)
{
static
const char *f = "phase %d";
size_t n_blocks, max, bit;
report (1, "calculating largest number of free blocks...");
/* Phase 1:
*
* Find greatest allocatable number-of-blocks such that
* it has only one bit set at '1' and '0' for the rest.
*/
report (2, f, 1);
n_blocks = ~(UINTPTR_MAX >> 1); // only MSB is set
max = UINTPTR_MAX / block_sz; // maximimum number of blocks
while (n_blocks && !(n_blocks & max))
n_blocks >>= 1;
while (try_alloc (n_blocks, block_sz) != n_blocks)
n_blocks >>= 1;
/* Phase 2:
*
* Starting with first allocatable number-of-blocks, add decreasingly
* significant bits to this value for each successful allocation.
*/
report (2, f, 2);
for ( bit = n_blocks >> 1; bit; bit >>= 1)
{
size_t n = n_blocks | bit;
if (try_alloc (n, block_sz) == n)
n_blocks = n;
}
/* Phase 3:
* For as long as allocation cannot be repeated,
* decrease number of blocks.
*/
report (2, f, 3);
while (try_alloc (n_blocks, block_sz) != n_blocks)
--n_blocks;
report (1, "free blocks: %zu", n_blocks);
return n_blocks;
}
size_t
try_alloc (size_t n_blocks,
size_t block_sz)
{
if (n_blocks != 0)
{
/* Try to allocate all of the requested blocks.
* If successful, return number of requested blocks;
* otherwise, return 0.
*/
void *p = calloc (n_blocks, block_sz);
report (3, "try %zu blocks of %zu bytes: %s",
n_blocks, block_sz, p ? "success" : "failure");
if (p)
{
free (p);
return n_blocks;
}
}
return 0;
}
#define MAX_INDENT 8
#define INDENT_SPACES " "
void
report (int indent,
const char *format,
...)
{
const char padding[MAX_INDENT+1] = INDENT_SPACES;
va_list args;
if (indent > MAX_INDENT)
indent = MAX_INDENT;
if (indent > 0)
printf ("%s", &padding[8-indent]);
va_start (args, format);
vprintf (format, args);
va_end (args);
printf ("n");
}
用法:
(<<p> count-free-blocks em> BLOCK_SIZE ]输入:
> ./count-free-blocks 33554432
输出:calculating free memory...
calculating largest number of free blocks...
phase 1
try 64 blocks of 33554432 bytes: success
phase 2
try 96 blocks of 33554432 bytes: failure
try 80 blocks of 33554432 bytes: success
try 88 blocks of 33554432 bytes: success
try 92 blocks of 33554432 bytes: failure
try 90 blocks of 33554432 bytes: success
try 91 blocks of 33554432 bytes: success
phase 3
try 91 blocks of 33554432 bytes: success
free blocks: 91
calculating largest number of free blocks...
phase 1
try 64 blocks of 33554432 bytes: success
phase 2
try 96 blocks of 33554432 bytes: failure
try 80 blocks of 33554432 bytes: success
try 88 blocks of 33554432 bytes: success
try 92 blocks of 33554432 bytes: failure
try 90 blocks of 33554432 bytes: success
try 91 blocks of 33554432 bytes: success
phase 3
try 91 blocks of 33554432 bytes: success
free blocks: 91
check once more...
try 91 blocks of 33554432 bytes: success
Available memory: 91 blocks of 33554432 bytes == 3053453312 bytes
我打算在我自己的应用程序中重新使用这些函数