C/C++/Linux

内存的分配与释放

2019-11-25  本文已影响0人  ninedreams

堆与栈

分配内存时堆与栈的区别

示例如下:

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语言中内存分配与释放

动态与静态分配内存
malloc和free函数
  1. malloc仅仅分配内存,free仅仅回收内存,并不执行构造和析构函数
  2. malloc、free是函数,可以覆盖,C、C++中都可以使用
  3. malloc申请的内存只能使用free释放
  4. malloc和free是实现的函数,并不是C语言的标准
malloc、calloc、realloc的使用

注意:如果调用成功,不管当前内存段后面的空闲空间是否满足要求,都会释放掉原来的指针,重新返回一个指针,虽然返回的指针有可能和原来的指针一样,即不能再次释放掉原来的指针。

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的内存占用更高,但是在多核多线程下的表现也最为优异

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

上一篇下一篇

猜你喜欢

热点阅读