我知道如何使用malloc()
和free()
分配内存,但是还有一个标准的 C 函数来检查剩余多少内存,所以我可以定期调用它来确保我的代码没有内存泄漏?
我唯一能想到的就是malloc(1)
在无限循环中调用,直到它返回错误,但不应该有更有效的方法吗?
我知道如何使用malloc()
和free()
分配内存,但是还有一个标准的 C 函数来检查剩余多少内存,所以我可以定期调用它来确保我的代码没有内存泄漏?
我唯一能想到的就是malloc(1)
在无限循环中调用,直到它返回错误,但不应该有更有效的方法吗?
不,没有标准的 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: %zu\n", GetFreeSize());
printf("Total free: %zu\n", GetFreeSize());
printf("Total free: %zu\n", GetFreeSize());
printf("Total free: %zu\n", GetFreeSize());
printf("Total free: %zu\n", GetFreeSize());
return 0;
}
输出(ideone):
Total free: 266677120
Total free: 266673024
Total free: 266673024
Total free: 266673024
Total free: 266673024
Linux glibcsysconf(_SC_AVPHYS_PAGES)
和get_avphys_pages()
这两个 glibc 扩展应该为您提供可用的页数。然后我们可以将它乘以页面大小sysconf(_SC_PAGESIZE)
来找到总可用内存。
主程序
#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%lX\n",
sysconf(_SC_PHYS_PAGES) * sysconf(_SC_PAGESIZE)
);
printf(
"sysconf(_SC_AVPHYS_PAGES) * sysconf(_SC_PAGESIZE) = 0x%lX\n",
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%lX\n",
get_phys_pages() * sysconf(_SC_PAGESIZE)
);
printf(
"get_avphys_pages() * sysconf(_SC_PAGESIZE) = 0x%lX\n",
get_avphys_pages() * sysconf(_SC_PAGESIZE)
);
}
编译并运行:
gcc -ggdb3 -O0 -std=c99 -Wall -Wextra -pedantic -o main.out main.c
./main.out
我的 32GiB 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
.
在 Ubuntu 19.04 中测试。
如果您负担得起#ifdef'ing 调试版本(可能在模拟器中!),您可以构建一个 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");
}
用法:
无计数块 [ 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
我打算将这些功能重新用于我自己的应用程序。