内存的分配与释放
堆与栈
- 堆是大家共有的空间,分全局堆和局部堆。全局堆就是所有没有分配的空间,局部堆就是用户分配的空间。堆在操作系统对进程初始化的时候分配,运行过程中也可以向系统要额外的堆,但是记得用完了要还给操作系统,要不然就是内存泄漏。
- 栈是线程独有的,保存其运行状态和局部自动变量的。栈在线程开始的时候初始化,每个线程的栈互相独立。每个函数都有自己的栈,栈被用来在函数之间传递参数。操作系统在切换线程的时候会自动的切换栈,就是切换SS/ESP寄存器。栈空间不需要在高级语言里面显式的分配和释放。
分配内存时堆与栈的区别
-
栈是由编译器自动分配释放,存放函数的参数值、局部变量的值等。操作方式类似于数据结构中的栈
-
堆一般由程序员分配释放,若不释放,程序结束时可能由OS回收。注意这里说是可能,并非一定。再强调一次,记得要释放!!!
-
栈区(stack) :
编译器自动分配释放,主要存放函数的参数值,局部变量值等。windows下,栈内存分配是一个常数,超出了限制,提示stack overflow错误 -
堆区(heap):程序员手动分配释放,操作系统80%内存
-
全局区或静态区:存放全局变量和静态变量;程序结束时由系统释放,分为全局初始化区和全局未初始化区
-
字符常量区:常量字符串放与此,程序结束时由系统释放
-
程序代码区:存放函数体的二进制代码
示例如下:
int a = 0; // 全局初始化区
char *p1; // 全局未初始化区
int main(int argc, char** argv) {
int b; // 栈
char s[] = "abc"; // 栈
char *p2; // 栈
char *p3 = "3210"; // 其中,"3210\0"常量区,p3在栈区
static int c = 0; // 全局区
p1 = (char*)malloc(20); // 20个字节区域在堆区
strcpy(p1, "3210"); // "3210\0" 在常量区,编译器可能会优化为和p3的指向同一块区域
return 0;
}
C语言中内存分配与释放
动态与静态分配内存
-
静态内存分配,分配内存大小的是固定,问题:1.很容易超出栈内存的最大值 2.为了防止内存不够用会开辟更多的内存,容易浪费内存
-
动态内存分配,在程序运行过程中,动态指定需要使用的内存大小,手动释放,释放之后这些内存还可以被重新使用
malloc和free函数
- malloc仅仅分配内存,free仅仅回收内存,并不执行构造和析构函数
- malloc、free是函数,可以覆盖,C、C++中都可以使用
- malloc申请的内存只能使用free释放
- malloc和free是实现的函数,并不是C语言的标准
malloc、calloc、realloc的使用
-
malloc() 函数
用来动态地分配内存空间,原型为:void* malloc (size_t size);
分配成功返回指向该内存的地址,失败则返回 NULL。 -
calloc() 函数
用来动态地分配内存空间并初始化为0
,就是NULL。原型为:void* calloc (size_t num, size_t size);
。calloc() 在内存中动态地分配 num 个长度为 size 的连续空间,并将每一个字节都初始化为 0。所以它的结果是分配了 num * size 个字节长度的内存空间,并且每个字节的值都是0。分配成功返回指向该内存的地址,失败则返回 NULL。 -
realloc() 函数
重新分配内存,原型为:realloc(void *__ptr, size_t __size);
,更改已经配置的内存空间,即更改由malloc()函数分配的内存空间的大小。如果将分配的内存减少,realloc仅仅是改变索引的信息。如果是将分配的内存扩大,则有以下情况:
1)如果当前内存段后面有需要的足够大内存空间,则直接扩展这段内存空间,realloc()将返回原指针。
2)如果当前内存段后面的空闲字节不够,那么就使用堆中的第一个能够满足这一要求的内存块,将目前的数据复制到新的位置,并将原来的数据块释放掉,返回新的内存块位置。
3)如果申请失败,将返回NULL,此时,原来的指针仍然有效。
注意:如果调用成功,不管当前内存段后面的空闲空间是否满足要求,都会释放掉原来的指针,重新返回一个指针,虽然返回的指针有可能和原来的指针一样,即不能再次释放掉原来的指针。
- reallocf(), valloc()函数
在mac系统中,还有这两个函数,不常用。原型为:
void* reallocf(void *ptr, size_t size);
void* valloc(size_t size);
-
The
valloc()
function allocates size bytes of memory and returns a pointer to the allocated memory. The allocated memory is aligned on a page boundary. -
The
reallocf()
function is identical to the realloc() function, except that it will free the passed pointer when the requested memory cannot be allocated.
This is a FreeBSD specific API designed to ease the problems with traditional coding styles for realloc causing memory leaks in libraries.
例子分析
#include <stdlib.h>
#include <stdio.h>
int main(int argc, char** argv) {
int a = 5;
long b = 10;
int * int_array = (int*)malloc(sizeof(int) * 8);
printf("%ld\n", int_array);
int_array[1] = 8;
free(int_array);
int * ints = (int*)malloc(sizeof(int) * 8);
printf("%ld\n", ints);
int * is = (int*)realloc(ints, sizeof(int) * 4);
printf("%ld\n", is);
int * iis = (int*)realloc(is, sizeof(int) * 16);
printf("%ld\n", iis);
//free(ints);
//free(is);
free(iis);
return 0;
}
输出结果如下:
由图可知,前三次的内存地址输出是一致的,第四次输出了不一样的内存地址。当然第二次的内存地址跟第一次输出的内存地址一样是因为编译优化的一些原因,大家可以尝试不同gcc版本,使用不同的-O级别。第三次输出一致的内存地址,是因为新分配的内存小于原来分配的内存长度,于是内存地址还是一样的,但是原来的指针已经被释放掉了。第四次则是内存不足,所以重新分配到了一个新的内存地址,原来的指针也没释放掉。读者可以把注释的free代码放开,自己编译看一下结果。
C++的new与delete
C++中是通过new和delete操作符进行动态内存管理。虽然是C++中的动态内存管理,但是与C语言中的四个函数不同,new和delete是C++中的操作符。标准中已经定义好,由编译器来实现分配和释放内存,不是函数库里面的实现,但是底层实现中也会调用基础的malloc和free函数。
new/delete和malloc/free的区别和联系:
- 它们都是动态管理内存的入口
- malloc/free是C/C++标准库的函数,new/delete是C++操作符
- malloc/free只是动态分配内存空间/释放空间。而new/delete除了分配空间还会调用构造析构函数进行初始化与清理(清理成员)
- malloc/free需要手动计算类型大小且返回值为void*,new/delete可自己计算类型的大小对应类型的指针
- new/delete的底层调用了malloc/free
- malloc/free申请空间后得判空,new/delete则不需要,之前是返回空指针,现在引发异常std:bad_alloc.
- new直接跟类型,malloc传入节数个数
例子分析
1.C+11之前,可以初始化简单变量:
int *p = new int(12);
delete p;
double *dp = new double(4.5);
delete[] dp;
2.C++11之后,可使用列表初始化:
struct point{double x,int y,doble z};
point *p = new point{1,2,3};
delete p;
int *array = new int []{1,2,3,4};
delete[] array;
3.使用new 和delete时,分别调用的是如下函数:
void * operator new(std::size_t);
void * operator new[](std::size_t);
void * operator delete(std::size_t);
void * operator delete[](std::size_t);
例如1中的示例:
int *p = new int;
int *p = new (sizeof(int));
第三方不同的malloc/free实现
C语言的标准函数库除了gcc libc
之外,还有许多其他的实现。这里专门介绍一下malloc的其他实现。主要是ptmalloc
,tcmalloc
,jemalloc
。这三种方式的实现各有自己的优缺点:
1.作为基础库的ptmalloc是最为稳定的内存管理器,无论在什么环境下都能适应,但是分配效率相对较低
2.而tcmalloc针对多核情况有所优化,性能有所提高,但是内存占用稍高,大内存分配容易出现CPU飙升
3.jemalloc的内存占用更高,但是在多核多线程下的表现也最为优异
具体需要使用哪一个库,需要按照自己的业务需求来,脱离业务需求谈技术的都是耍流氓。