C++面试基础知识:new vs malloc

new vs malloc

C++中有operator new和new expression,两者不一样,operator new跟malloc类似,只用于分配内存,new expression则是常用的new一个对象。后者调用前者分配内存,并运行constructor进行初始化。
同样的,也存在operator delete 和 delete expression。

什么时候用new or malloc:
当需要一个对象的生命周期超过他本身的scope时

  • new是C++中的操作符,malloc是C语言的库函数。
  • 调用方式:new自动计算所需内存大小,malloc需要指定分配内存大小
  • 返回值:new返回对象类型的指针,malloc返回void*指针
  • 分配失败:new抛出异常std::bad_alloc,malloc返回NULL
  • 分配区域:malloc在堆上分配内存,new默认在堆上,但是可以重载operator new来自定义内存分配行为,所以不一定是在堆上。
  • 实现:malloc先在堆上进行内存分配,如果剩余内存不够,会调用sbrkmmap系统调用来扩大内存,当所需内存小时调用sbrk,当所需内存大时调用mmapsbrk是直接移动heap的边界来扩展内存,mmap是直接在内存映射区分配内存。
    • malloc背后还维护了一个内存池,每次进行系统调用时,申请的空间是大于我们需要的空间的,free时也不会立刻把内存还给系统。这很容易理解,频繁的系统调用非常影响性能,维护一个内存池更好一些。
    • 但是如果是使用mmap得到的内存,free时会立刻还回去。
  • 释放:malloc对应free,new对应delete,不能混用。delete会先调用析构函数,再调用operator delete。malloc分配时会在这段内存的头部多分配出来一块头块,用于存放内存管理的信息,包括该部分内存长度,指向上/下一个block的指针等,free会将指针左移到头部根据内存管理信息释放内存,而且也分配和释放时也不一定是完全按照指定size大小分配的,有可能为了对齐会分配为2的倍数。

代码:

struct TempStruct
{
    /* data */
    int a;
    float b;
    char c;
    TempStruct() : a(0), b(0.0), c('0') {std::cout << "TempStruct" << std::endl;}
    ~TempStruct() {std::cout << "~TempStruct" << std::endl;}

    static void* operator new(size_t size) //operator new可以重载,但是new expression不能重载
    {
        std::cout << "new" << std::endl;
        return malloc(size);
    }
    static void operator delete(void* p) //operator delete可以重载,但是delete expression不能重载
    {
        std::cout << "delete" << std::endl;
        free(p);
    }
};

void* GetValue()
{
    void* p = malloc(sizeof(TempStruct)); //当需要该对象的生命周期超出作用域时,才使用malloc/new
    TempStruct* p2 = new TempStruct; //new expression
    delete p2;
    return p;
}
int main()
{
    TempStruct* p = static_cast<TempStruct*>(GetValue());
    free(p);
    try {
        // 尝试分配一个非常大的内存块,可能会导致失败
        TempStruct* ptr = new TempStruct[10000000000000];
        std::cout << "分配成功" << std::endl;
        delete[] ptr;
    } catch (const std::bad_alloc& e) {
        std::cout << "内存分配失败: " << e.what() << std::endl;
    }
    return 0;
}

扩展:

malloc分配是分配虚拟内存,在没有使用时不一定有与物理内存的映射,这与操作系统的策略有关。在第一次访问时会产生缺页中断,操作系统会介入,查找可用的物理内存页,并将其分配给该虚拟内存页,同时更新页表以建立虚拟内存与物理内存之间的映射关系。

new的出现就是为了满足RCII准则,但是上面我们写的代码还是不满足RCII,如果当我们new/malloc后,还没有等到delete/free时代码就出bug崩了,那就会出现memory leak,好的办法是直接将new或者malloc出来的指针放到智能指针里。

因为new本质是分两步,所以new不是线程安全的,需要人为保证new操作的线程安全。

多线程下的内存分配问题:
对于ptmalloc分配器,多线程时,会分为主分配区和从分配区:
主分配区一个进程只能有一个,其是调用brk,从堆区去分配内存。
从分配区一个进程程可以拥有多个从分配区,其调用mmap从memory mapping区域去分配一个sub-heap
原因:因为数据竞争问题,在一个线程使用堆时,其他线程不能访问,如果把其他线程挂起来,那多线程速度也会受影响,所以ptmalloc就定义了per thread arena,每次只对使用到的arena进行加锁,以实现更细粒度的互斥。性能更好。thread arena不是每个线程一个,而是受核心数量限制的。
PS:
内存分配器有很多不同的实现,上面谈到的ptmalloc是glibc中内置的内存分配器。
还有很多开源的其他实现,例如Google开源了一个tcmalloc,自定义了malloc和C++的operator new

Ref

How does malloc work in a multithreaded environment
Difference between ‘new operator’ and ‘operator new’?
深入理解 glibc malloc:内存分配器实现原理

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值