Linux 内存分配/内存管理 相关接口
一个进程可以通过增加堆的尺寸来分配内存。堆是一个用来存放动态分配的变量的空间,它位于未初始化数据段(bss)之后,它的顶叫做 `program break` ,这个地方会根据内存的分配和释放而变化。一般来讲C语言堆内存的分配一般会使用 `malloc()` ,它是基于 `brk()和sbrk()` 所实现的。
Linux 内存分配/内存管理 相关接口
一个进程可以通过增加堆的尺寸来分配内存。堆是一个用来存放动态分配的变量的空间,它位于未初始化数据段(bss)之后,它的顶叫做 program break
,这个地方会根据内存的分配和释放而变化。一般来讲C语言堆内存的分配一般会使用 malloc()
,它是基于 brk()和sbrk()
所实现的。
Linux GNU C 中动态内存分配相关的接口如下:
void *malloc (size_t size)
分配一个 size 字节的块。
void free (void *addr)
释放之前由 malloc 分配的块。
void *realloc (void *addr, size_t size)
使以前由 malloc 分配的块更大或更小,可能通过将其复制到新位置。
void *reallocarray (void *ptr, size_t nmemb, size_t size)
将 malloc 先前分配的块的大小更改为 nmemb * size 个字节,与 realloc 一样。
void *calloc (size_t count, size_t eltsize)
使用 malloc 分配一个 count * eltsize 字节块,并将其内容设置为零。
void *valloc (size_t size)
从页边界开始分配大小字节块。
void *aligned_alloc (size_t size, size_t alignment)
从 alignment 倍数的地址开始分配 size 字节块。
int posix_memalign (void **memptr, size_t alignment, size_t size)
从 alignment 倍数的地址开始分配 size 字节块。
void *memalign (size_t size, size_t boundary)
分配一个 size 字节的块,从一个 boundary 倍数的地址开始。
int mallopt (int param, int value)
调整可调参数。
int mcheck (void (*abortfn) (void))
告诉 malloc 对动态分配的内存执行偶尔的一致性检查,并在发现不一致时调用 abortfn。
struct mallinfo2 mallinfo2 (void)
返回有关当前动态内存使用情况的信息。
下面将逐一介绍:
分配栈内存
alloca()
函数 alloca() 支持一种半动态分配,其中块是动态分配但自动释放的。 使用 alloca()
分配块是一个显式操作;您可以根据需要分配任意数量的块,并在运行时计算大小。但是,当您退出调用 alloca()
的函数时,所有块都会被释放, 就像它们是在该函数中声明的自动变量一样。没有办法明确地释放空间 。
#include <stdlib.h>
// size: 分配size个字节大小的内存空间
void * alloca (size t size)
返回值 : 成功将返回内存空间的指针。
不要在函数调用的参数中使用 alloca()
——你会得到不可预知的结果,因为 alloca()
的栈空间会出现在函数参数空间的中间的栈上。
要避免的一个例子是:
foo (x, alloca (4), y);
可以修改为:
void *y;
y = alloca(size);
func(x, y, z);
alloca()
与 malloc()
对比 :
int
open2 (char *str1, char *str2, int flags, int mode)
{
char *name = (char *) alloca (strlen (str1) + strlen (str2) + 1);
stpcpy (stpcpy (name, str1), str2);
return open (name, flags, mode);
}
int
open2 (char *str1, char *str2, int flags, int mode)
{
char *name = malloc (strlen (str1) + strlen (str2) + 1);
int desc;
if (name == 0)
fatal ("virtual memory exceeded");
stpcpy (stpcpy (name, str1), str2);
desc = open (name, flags, mode);
free (name);
return desc;
}
作为使用 alloca
的示例,这里有一个函数,它打开一个由两个参数字符串连接而成的文件名,并返回一个文件描述符或-1表示失败。如上所见,使用 alloca
更简单。但是 alloca
还有其他更重要的优点和一些缺点:
alloca
可能优于 malloc
的原因 :
- 使用
alloca
浪费的空间非常小,而且速度非常快。(它由 GNU C 编译器开放编码。)。 - 由于
alloca
没有用于不同大小的块的单独池,因此用于任何大小块的空间都可以重用于任何其他大小。alloca
不会导致内存碎片。 - 使用
longjmp
完成的非本地退出在通过调用alloca
的函数退出时自动释放使用alloca
分配的空间。这是使用alloca
的最重要原因。
为了说明这一点,假设您有一个函数 open_or_report_error 如果成功则返回一个描述符,如 open,但如果失败则不返回其调用者。如果文件无法打开,它会打印一条错误消息并使用 longjmp 跳到程序的命令级别。让我们更改 open2 以使用此子例程:
int
open2 (char *str1, char *str2, int flags, int mode)
{
char *name = (char *) alloca (strlen (str1) + strlen (str2) + 1);
stpcpy (stpcpy (name, str1), str2);
return open_or_report_error (name, flags, mode);
}
由于 alloca
的工作方式,即使发生错误,它分配的内存也会被释放,无需特别努力。
相比之下,如果以这种方式更改 open2 的先前定义(使用 malloc 和 free),则会产生内存泄漏。即使您愿意进行更多更改来修复它,也没有简单的方法可以做到这一点。
alloca
与 malloc
相比的缺点 :
- 如果您尝试分配的内存超出机器所能提供的范围,您将不会收到干净的错误消息。相反,你会得到一个致命的信号,就像你从无限递归中得到的一样;可能是分段违规(请参阅程序错误信号)。
- 一些非 GNU 系统不支持 alloca,因此它的可移植性较差。然而,用 C 语言编写的 alloca 的较慢仿真可用于具有此缺陷的系统。
GNU C可变大小数组 :
在 GNU C 中,您可以用一个可变大小的数组来替换大多数使用的 alloca
。下面是 open2 的样子:
int open2 (char *str1, char *str2, int flags, int mode)
{
char name[strlen (str1) + strlen (str2) + 1];
stpcpy (stpcpy (name, str1), str2);
return open (name, flags, mode);
}
但是 alloca
并不总是等价于可变大小的数组,原因如下:
-
可变大小数组的空间在数组名称范围的末尾被释放。用
alloca
分配的空间一直保留到函数结束。 -
可以在循环中使用
alloca
,在每次迭代时分配一个额外的块。这对于可变大小的数组是不可能的。
注意:如果在一个函数中混合使用 alloca
和可变大小的数组,退出声明了可变大小数组的作用域会释放在该作用域执行期间分配有 alloca
的所有块。
分配堆内存
直接分配
malloc()
malloc()
的函数原型如下:
#include <stdlib.h>
// size:需要分配的内存大小,以字节为单位
void *malloc(size_t size);
返回值 :返回值为 void *
类型,如果申请分配内存成功,将返回一个指向该段内存的指针, void *
并不是说没有返回值或者返回空指针,而是返回的指针类型未知,所以在调用 malloc()
时通常需要进行强制类型转换,将 void *
指针类型转换成我们希望的类型 ; 如果分配内存失败(譬如系统堆内存不足)将返回 NULL
,如果参数size为0,返回值也是 NULL
。
如果没有更多可用空间,则 malloc
返回一个空指针。您应该检查每次调用 malloc
的值。编写一个调用 malloc
并在值为空指针时报告错误的子例程很有用,仅当值非零时才返回。该函数通常称为 xmalloc
:
void *xmalloc (size_t size)
{
void *value = malloc (size);
if (value == 0)
fatal ("virtual memory exhausted");
return value;
}
这是一个使用 malloc
的真实示例(通过 xmalloc
)。函数 savestring
会将一系列字符复制到新分配的以空字符结尾的字符串中:
char *savestring (const char *ptr, size_t len)
{
char *value = xmalloc (len + 1);
value[len] = '\0';
return memcpy (value, ptr, len);
}
malloc
为您提供的块保证是对齐的,以便它可以保存任何类型的数据。在 GNU 系统上,地址在 32 位系统上始终是 8 的倍数,在 64 位系统上始终是 16 的倍数 。很少需要任何更高的边界(例如页面边界);对于这些情况,请使用 aligned_alloc
或 posix_memalign
。
分配初始化空间
calloc()
calloc()
函数用来动态地分配内存空间并初始化为0,其函数原型如下所示:
#include <stdlib.h>
// 在堆中申请nmemb个长度为size的连续空间,并将每一个字节都初始化为0
void *calloc(size_t nmemb, size_t size);
返回值 :分配成功返回指向该内存的地址,失败则返回 NULL
calloc()
与 malloc()
的一个重要区别是: calloc()
在动态分配完内存后,自动初始化该内存空间为零 , 而 malloc()
不初始化,里边数据是未知的垃圾数据 。下面的两种写法是等价的:
// calloc()分配内存空间并初始化
char *buf1 = (char *)calloc(10, 2);
// malloc()分配内存空间并用memset()初始化
char *buf2 = (char *)malloc(10 * 2);
memset(buf2, 0, 20);
使用示例:
#include <stdio.h>
#include <stdlib.h>
int main(int argc, char *argv[])
{
int *base = NULL;
int i;
/* 校验传参 */
if (2 > argc) exit(-1);
/* 使用calloc申请内存 */
base = (int *)calloc(argc - 1, sizeof(int));
if (NULL == base)
{
printf("calloc error\n");
exit(-1);
}
/* 将字符串转为int型数据存放在base指向的内存中 */
for (i = 0; i < argc - 1; i++)
base[i] = atoi(argv[i+1]);
/* 打印base数组中的数据 */
printf("你输入的数据是: ");
for (i = 0; i < argc - 1; i++)
printf("%d ", base[i]); putchar('\n');
/* 释放内存 */
free(base);
exit(0);
}
运行结果:
分配对齐空间
posix_memalign()
函数原型如下:
#include <stdlib.h>
// memptr:void **类型的指针,内存申请成功后会将分配的内存地址存放在*memptr中
// alignment:设置内存对其的字节数,alignment必须是2的幂次方(譬如2^4、2^5、2^8等),同时也要是sizeof(void *)的整数倍
int posix_memalign(void **memptr, size_t alignment, size_t size);
posix_memalign()
函数用于在堆上分配 size
个字节大小的对齐内存空间,将 *memptr
指向分配的空间,分配的内存地址将是参数 alignment
的整数倍。 参数 alignment
表示对齐字节数, alignment
必须是2的幂次方(譬如24、25、2^8等),同时也要是 sizeof(void *)
的整数倍,对于32位系统来说, sizeof(void *)
等于4,如果是64位系统 sizeof(void *)
等于8。
返回值 :成功将返回0;失败返回非0值
使用示例:
#include <stdio.h>
#include <stdlib.h>
int main(int argc, char *argv[])
{
int *base = NULL;
int ret;
/* 申请内存: 256字节对齐 */
ret = posix_memalign((void **)&base, 256, 1024);
if (0 != ret)
{
printf("posix_memalign error\n");
exit(-1);
}
/* 使用内存 */
// base[0] = 0;
// base[1] = 1;
// base[2] = 2;
// base[3] = 3;
/* 释放内存 */
free(base);
exit(0);
}
aligned_alloc()
aligned_alloc()
函数用于分配size个字节大小的内存空间,返回指向该空间的指针 。此函数是在 ISO C11 中引入的,因此 可能比 posix_memalign()
对现代非 POSIX 系统具有更好的可移植性 。
函数原型如下:
#include <stdlib.h>
// alignment:用于设置对齐字节大小,alignment必须是2的幂次方(譬如2^4、2^5、2^8等)
// size:设置分配的内存大小,以字节为单位。参数size必须是参数alignment的整数倍
void *aligned_alloc(size_t alignment, size_t size);
返回值 :成功将返回内存空间的指针,内存空间的起始地址是参数alignment的整数倍;失败返回NULL。
使用示例:
#include <stdio.h>
#include <stdlib.h>
int main(int argc, char *argv[])
{
int *base = NULL;
/* 申请内存: 256字节对齐 */
base = (int *)aligned_alloc(256, 256 * 4);
if (base == NULL)
{
printf("aligned_alloc error\n");
exit(-1);
}
/* 使用内存 */
// base[0] = 0;
// base[1] = 1;
// base[2] = 2;
// base[3] = 3;
/* 释放内存 */
free(base);
exit(0);
}
过时:memalign()
memalign()
函数已过时,应改用 aligned_alloc()
或 posix_memalign()
。
分配一个由 size
指定大小,地址是 alignment
的倍数的内存块。 memalign()
与 aligned_alloc()
参数是一样的,它们之间的区别在于:对于参数 size
必须是参数 alignment
的整数倍这个限制条件, memalign()
并没有这个限制条件。
#include <malloc.h>
// alignment:用于设置对齐字节大小, alignment 必须是 2 的幂次方(譬如 2^4、 2^5、 2^8 等)
// size: 设置分配的内存大小,以字节为单位。参数 size 必须是参数 alignment 的整数倍
void *memalign(size_t alignment, size_t size);
使用示例:
#include <stdio.h>
#include <stdlib.h>
#include <malloc.h>
int main(int argc, char *argv[])
{
int *base = NULL;
/* 申请内存: 256字节对齐 */
base = (int *)memalign(256, 1024);
if (base == NULL)
{
printf("memalign error\n");
exit(-1);
}
/* 使用内存 */
// base[0] = 0;
// base[1] = 1;
// base[2] = 2;
// base[3] = 3;
/* 释放内存 */
free(base);
exit(0);
}
过时:valloc()
valloc()
函数已过时,应改用 aligned_alloc()
或 posix_memalign()
。
分配 size 个字节大小的内存空间,返回指向该内存空间的指针, 内存空间的地址是页大小(pagesize) 的倍数。 使用 valloc
就像使用 memalign
并将页面大小作为第一个参数的值传递。它是这样实现的:
#include <malloc.h>
// size: 分配size个字节大小的内存空间
void *valloc(size_t size);
返回值 : 成功将返回内存空间的指针。
valloc()
与 memalign()
类似, 只不过 valloc()
函数内部实现中,使用了页大小作为对齐的长度 。
在程序当中,可以通过系统调用 getpagesize()
来获取内存的页大小。
#include <stdio.h>
#include <stdlib.h>
int main(int argc, char *argv[])
{
int *base = NULL;
/* 申请内存: 1024个字节 */
base = (int *)valloc(1024);
if (base == NULL)
{
printf("valloc error\n");
exit(-1);
}
/* 使用内存 */
// base[0] = 0;
// base[1] = 1;
// base[2] = 2;
// base[3] = 3;
/* 释放内存 */
free(base);
exit(0);
}
过时:pvalloc()
pvalloc
与 valloc
相似,不过将分配的空间大小扩展为页大小的倍数。
#include <malloc.h>
// size: 分配size个字节大小的内存空间
void *pvalloc(size_t size);
返回值 : 成功将返回内存空间的指针。
修改块的大小
当使用块时,通常无法确定最终需要多大的块。例如,块可能是一个缓冲区,用于保存从文件中读取的行;无论最初制作缓冲区多长时间,都可能遇到更长的行。 此时就可以通过调用 realloc()
或 reallocarray()
使块更长 。这些函数在 stdlib.h
中声明。
realloc()
#include <stdlib.h>
// 将地址为 ptr 的块的大小更改为 newsize
void * realloc (void *ptr, size t newsize)
由于块末尾之后的空间可能正在使用中, realloc()
可能会发现有必要将块复制到有更多可用空间的新地址 。 realloc()
的返回值是块的新地址。如果需要移动块, realloc()
会复制旧的内容。
如果你为 ptr 传递一个空指针, realloc()
的行为就像 malloc (newsize)
。否则,如果 newsize 为零,realloc 释放块并返回 NULL。否则,如果 realloc 无法重新分配请求的大小,则返回 NULL 并设置 errno;原始块不受干扰。
reallocarray()
#include <stdlib.h>
// 将地址为 ptr 的块的大小更改为足够长以包含 nmemb 元素的向量(vector),每个元素的大小为size。
void * reallocarray (void *ptr, size t nmemb, size t size)
reallocarray
函数等效于 realloc (ptr, nmemb * size)
,但如果乘法溢出, reallocarray
会安全失败,方法是将 errno
设置为 ENOMEM
,返回一个空指针,并保持原始块不变。
当分配块的新大小是可能溢出的乘法结果时,应使用 reallocarray
而不是 realloc
。
与 malloc
一样,如果没有可用的内存空间使块变大, realloc
和 reallocarray
可能会返回空指针。 发生这种情况时,原始块保持不变;它没有被修改或搬移 。
在大多数情况下,当 realloc
失败时,原始块发生的情况并没有什么不同,因为应用程序在内存不足时无法继续,唯一要做的就是给出一个致命的错误消息。编写和使用通常称为 xrealloc
和 xreallocarray
的子例程通常很方便,它们像 xmalloc
对 malloc
所做的那样处理错误消息:
void *xreallocarray (void *ptr, size_t nmemb, size_t size)
{
void *value = reallocarray (ptr, nmemb, size);
if (value == 0)
fatal ("Virtual memory exhausted");
return value;
}
void *xrealloc (void *ptr, size_t size)
{
return xreallocarray (ptr, 1, size);
}
您还可以使用 realloc
或 reallocarray
使块更小。这样做的原因是为了避免在只需要一点内存空间时占用大量内存空间。在几种分配实现中,有时需要复制一个块,因此如果没有其他可用空间,它可能会失败。
修改内存分配参数
mallopt()
可以使用 mallopt
函数调整动态内存分配的一些参数。该函数是通用的 SVID/XPG
接口,定义在 malloc.h
中。
#include <malloc.h>
// param: 指定要设置的参数
// value: 要设置的新值
int mallopt (int param, int value)
param
的选择有如下:
参数 | 作用 |
---|---|
M_MMAP_MAX | 使用 mmap 分配的最大块数。 将此设置为零将禁用所有 mmap 的使用。该参数的默认值为 65536。 通过将环境变量 MALLOC_MMAP_MAX_ 设置为所需的值,也可以在启动时为进程设置此参数。 |
M_MMAP_THRESHOLD | 使用 mmap 系统调用将所有大于此值的块分配到正常堆之外。 这样可以保证这些块的内存 free 后可以返回给系统。请注意,小于此阈值的请求仍可能通过 mmap 分配。 如果未设置此参数,则默认值设置为 128 KiB,并动态调整阈值以适应程序的分配模式。如果设置了参数,则禁用动态调整,并将值静态设置为输入值。 通过将环境变量 MALLOC_MMAP_THRESHOLD_ 设置为所需的值,也可以在启动时为进程设置此参数。 |
M_PERTURB | 如果非零,则在分配内存块(由 calloc 分配时除外)和释放时,根据此参数的某些低位的位填充内存块。 这可用于调试未初始化或已释放堆内存的使用。请注意,此选项不保证释放的块将具有任何特定值。它只保证块被释放之前的内容将被覆盖。 此参数的默认值为 0。 通过将环境变量 MALLOC_PERTURB_ 设置为所需的值,也可以在启动时为进程设置此参数。 |
M_TOP_PAD | 此参数确定当需要扩展 arena 时从系统获得的额外内存量。 它还指定缩小 arena 时要保留的字节数。这提供了堆大小的必要滞后,从而可以避免过多的系统调用。 此参数的默认值为 0。 通过将环境变量 MALLOC_TOP_PAD_ 设置为所需的值,也可以在启动时为进程设置此参数。 |
M_TRIM_THRESHOLD | 这是最顶层的可释放块的最小大小(以字节为单位),它将触发系统调用以将内存返回给系统。 如果未设置此参数,则默认值设置为 128 KiB,并动态调整阈值以适应程序的分配模式。如果设置了参数,则禁用动态调整,并将值静态设置为提供的输入。 通过将环境变量 MALLOC_TRIM_THRESHOLD_ 设置为所需的值,也可以在启动时为进程设置此参数。 |
M_ARENA_TEST | 该参数指定在对 arena 数量限制进行测试之前可以创建的 arena 数量。 如果设置了 M_ARENA_MAX,则忽略该值。 此参数的默认值在 32 位系统上为 2,在 64 位系统上为 8。 通过将环境变量 MALLOC_ARENA_TEST 设置为所需的值,也可以在启动时为进程设置此参数。 |
M_ARENA_MAX | 此参数设置要使用的 arena 数量,而与系统中的核心数量无关。 此可调参数的默认值为 0,这意味着对 arenas 数量的限制由在线 CPU 内核数决定。对于 32 位系统,限制是在线内核数的两倍,在 64 位系统上,它是八倍的核心在线数量。请注意,默认值并非源自 M_ARENA_TEST 的默认值,而是独立计算的。 通过将环境变量 MALLOC_ARENA_MAX 设置为所需的值,也可以在启动时为进程设置此参数。 |
检查内存一致性
mcheck()
可以使用 mcheck
函数要求 malloc
检查动态内存的一致性,并使用 LD_PRELOAD
环境变量预加载 malloc
调试库 libc_malloc_debug
。这个函数是一个 GNU 扩展,在 mcheck.h
中声明。
#include <mcheck.h>
// abortfn: 一致性检查出错时调用的函数
int mcheck (void (*abortfn) (enum mcheck status status))
调用 mcheck
告诉 malloc
执行偶尔的(occasional)一致性检查 。这些将捕获诸如写入超过使用 malloc
分配的块的末尾之类的东西。 abortfn
参数是发现不一致时调用的函数。如果您提供一个空指针,则 mcheck
使用一个默认函数,该函数打印一条消息并调用 abort
(请参阅终止程序)。您提供的函数使用一个参数调用,该参数说明检测到哪种不一致;其类型如下所述。
一旦你用 malloc
分配了任何东西,再开始分配检查为时已晚, mcheck
必须在第一个内存分配函数之前调用。 一旦在之后调用, mcheck
在这种情况下什么都不做。如果调用太晚,该函数返回 -1,否则返回 0(成功时)。
安排尽早调用 mcheck
的最简单方法是在链接程序时使用选项 “-lmcheck”
;那么你根本不需要修改你的程序源。 或者,您可以使用调试器在程序启动时插入对 mcheck
的调用,例如,这些 gdb 命令将在程序启动时自动调用 mcheck
:
(gdb) break main
Breakpoint 1, main (argc=2, argv=0xbffff964) at whatever.c:10
(gdb) command 1
Type commands for when breakpoint 1 is hit, one per line.
End with a line saying just "end".
>call mcheck(0)
>continue
>end
(gdb) ...
mprobe()
#include <mcheck.h>
// pointer: 检查地址指针,必须是 malloc 或 realloc 返回的指针
enum mcheck_status mprobe (void *pointer)
mprobe
函数允许显式检查特定分配块中的不一致。 必须已经在程序开始时调用了 mcheck
来进行偶尔的检查;调用 mprobe
请求在调用时进行额外的一致性检查 。
返回值 : enum mcheck_status
,包含以下类型:
枚举类型 | 意义 |
---|---|
MCHECK_DISABLED | 在第一次分配之前没有调用 mcheck 。无法进行一致性检查 |
MCHECK_OK | 未检测到不一致 |
MCHECK_HEAD | 块之前的数据被立即修改。这通常发生在数组索引或指针减少太多时 |
MCHECK_TAIL | 块之后的数据被立即修改。当数组索引或指针增加太多时,通常会发生这种情况 |
MCHECK_FREE | 该块已被释放 |
读取内存使用信息
mallinfo2()
可以 调用 mallinfo2
函数获取有关动态内存分配的信息 。
#include <malloc.h>
struct mallinfo2 mallinfo2 (void)
数据类型:struct mallinfo2
struct mallinfo2
{
size_t arena; /* 从系统分配的未映射空间。malloc 用 sbrk 分配的内存的总大小,以字节为单位。 */
size_t ordblks; /* 可用块的数量。内存分配器从操作系统内部获取内存块大小,然后将它们分割以满足各个 malloc 请求 */
size_t smblks; /* fastbin块的数量 */
size_t hblks; /* 使用 mmap 分配的块的总数 */
size_t hblkhd; /* 使用 mmap 分配的内存的总大小,以字节为单位 */
size_t usmblks; /* 始终为0,为向后兼容而保留 */
size_t fsmblks; /* 释放的fastbin块中的可用空间 */
size_t uordblks; /* malloc 分配的块所占用的内存总大小 */
size_t fordblks; /* 空闲(未使用)块占用的内存总大小 */
size_t keepcost; /* 通常与堆末端接壤的最高可释放块的大小(即虚拟地址空间数据段的高端) */
};
malloc_usable_size()
可以 调用 malloc_usable_size
函数获取用户可用的空间大小 。
#include <malloc.h>
// __ptr: 检查地址指针
size_t malloc_usable_size (void *__ptr)
返回值 :可用空间大小
释放空间
free()
在堆上分配的内存,需要开发者自己手动释放掉,通常使用 free()
函数释放堆内存, free()
函数原型如下所示:
#include <stdlib.h>
// ptr:指向需要被释放的堆内存对应的指针
void free(void *ptr);
返回值 :无返回值
使用示例:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#define MALLOC_MEM_SIZE (1 * 1024 * 1024)
int main(int argc, char *argv[ ])
{
char *base = NULL;
/*申请堆内存*/
base = (char *)malloc(MALLOC_MEM_SIZE);
if (NULL == base){
printf("malloc error \n");
exit(-1);
}
/*初始化申请到的堆内存 */
memset(base, 0x0, MALLOC_MEM_SIZE);
/*使用内存*/
/*………………*/
/*释放内存*/
free(base);
exit(0);
}
由于 Linux 系统中,当一个进程终止时,内核会自动关闭它没有关闭的所有文件(该进程打开的文件,但是在进程终止时未调用close()关闭它)。同样,对于内存来说,也是如此! 当进程终止时,内核会将其占用的所有内存都返还给操作系统,这包括在堆内存中由malloc()函数所分配的内存空间 。基于内存的这一自动释放机制,很多应用程序通常会省略对free()函数的调用。
这在程序中分配了多块内存的情况下可能会特别有用,因为加入多次对free()的调用不但会消耗品大量的CPU时间,而且可能会使代码趋于复杂。
虽然依靠终止进程来自动释放内存对大多数程序来说是可以接受的,但最好能够在程序中显式调用 free()
释放内存,原因有二:
- 显式调用
free()
能使程序具有更好的 可读性和可维护性 ; - 对于很多程序来说, 申请的内存并不是在程序的生命周期中一直需要 ,大多数情况下,都是根据代码需求动态申请、释放的,如果申请的内存对程序来说已经不再需要了,那么就已经把它释放、归还给操作系统, 如果持续占用,将会导致内存泄漏 ,也就是人们常说的“你的程序在吃内存”!
参考文献
1:glibc 知:手册03:虚拟地址分配和分页_xmalloc_canpool的博客-CSDN博客
2:7. Linux的内存分配_linux内存分配_猴子头头123的博客-CSDN博客
3:linux C编程4-系统信息/时间/内存分配/随机数/定时器__sc_pagesize_邻居家的小南瓜的博客-CSDN博客
4:【正点原子】STM32MP1嵌入式Linux C应用编程指南V1.4 - 章节7.6
5:Memory-Allocation——The GNU C Library
如有疑问或错误,欢迎和我私信交流指正。
版权所有,未经授权,请勿转载!
Copyright © 2023.05 by Mr.Idleman. All rights reserved.
更多推荐
所有评论(0)