内存的分配与释放

堆与栈

  • 堆是大家共有的空间,分全局堆和局部堆。全局堆就是所有没有分配的空间,局部堆就是用户分配的空间。堆在操作系统对进程初始化的时候分配,运行过程中也可以向系统要额外的堆,但是记得用完了要还给操作系统,要不然就是内存泄漏。
  • 栈是线程独有的,保存其运行状态和局部自动变量的。栈在线程开始的时候初始化,每个线程的栈互相独立。每个函数都有自己的栈,栈被用来在函数之间传递参数。操作系统在切换线程的时候会自动的切换栈,就是切换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函数
  1. malloc仅仅分配内存,free仅仅回收内存,并不执行构造和析构函数
  2. malloc、free是函数,可以覆盖,C、C++中都可以使用
  3. malloc申请的内存只能使用free释放
  4. 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);
  1. 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.

  2. 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的区别和联系:
  1. 它们都是动态管理内存的入口
  2. malloc/free是C/C++标准库的函数,new/delete是C++操作符
  3. malloc/free只是动态分配内存空间/释放空间。而new/delete除了分配空间还会调用构造析构函数进行初始化与清理(清理成员)
  4. malloc/free需要手动计算类型大小且返回值为void*,new/delete可自己计算类型的大小对应类型的指针
  5. new/delete的底层调用了malloc/free
  6. malloc/free申请空间后得判空,new/delete则不需要,之前是返回空指针,现在引发异常std:bad_alloc.
  7. 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的内存占用更高,但是在多核多线程下的表现也最为优异

具体需要使用哪一个库,需要按照自己的业务需求来,脱离业务需求谈技术的都是耍流氓。

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 194,390评论 5 459
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 81,821评论 2 371
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 141,632评论 0 319
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 52,170评论 1 263
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 61,033评论 4 355
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 46,098评论 1 272
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 36,511评论 3 381
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 35,204评论 0 253
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 39,479评论 1 290
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 34,572评论 2 309
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 36,341评论 1 326
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 32,213评论 3 312
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 37,576评论 3 298
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 28,893评论 0 17
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 30,171评论 1 250
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 41,486评论 2 341
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 40,676评论 2 335

推荐阅读更多精彩内容