• 动态内存开辟(一)


    malloc的申请方式

        1.具有长生命周期的大内存将使用mmap分配

        2.对于短生命周期的内存分配将使用brk系统调用。

        3.对于小内存块的释放将返回到bin数组下,大内存(使用mmap分配的)将直接返回给操作系统。

        4.小内存块的合并(切割)仅仅在malloc和free的时候,并且合并(切割)以后也不一定返回给操作系统

        5.设置了若干阀值来避免内存暴增现象

        6.为了支持多线程,多个线程可以从同一个分配区分配内存,但是会使用锁来保证线程安全。同时为了优化算法,在出现内存不够的时候会重新创建一个分配区。

    一. malloc的底层实现原理ptmalloc ,tcmalloc、

    【1】少于128K的内存malloc

    int main()

    {

      void *p=malloc(4);

      free(p);

      return 0;

    }

    【2】大于128K的内存malloc

    int main()

    {

      void *p=malloc(1024*128);

      free(p);

      return 0;

    }

    在Linux上用gcc编译代码【1】,通过strace命令跟踪可执行程序(#strace ./a.out)

    当你通过malloc申请小于128K个字节的内存时,linux内核上是通过do_brk这个系统调用来实现的,而且你会发现,你通过malloc只想申请4个字节的内存,内核却把brk指针

    移动了好几个内存页面,当然malloc只会给你返回4字节,其他的大片内存他就要自己管理起来,下次你再申请时就不用深入内核了,一切为了效率!那么想知道malloc是怎

    么把剩余的堆内存管理起来的呢?对了,这就是ptmalloc的实现原理,通过bins和chunk机制来实现内存的管理机制,你还可以看到,通过brk系统调用申请的内存在应用程序

    free时,并没有归还给操作系统,那归还给谁了?答案是归还给了ptmalloc,详细的内存管理机制非常精彩

    同时在Linux上编译代码【2】,大家会发现申请>=128K的堆内存时,Linux内核上是通过mmap函数申请的,在free时是对应的内核调用函数munmap也被调用了

    //  深入理解do_brk,mmap,munmap;函数

    在c和c++中的动态内存分配

    【1】malloc和new的区别?

    (1)malloc 申请内存在堆上,new在自由存储区(堆和静态存储区)申请内存并初始化

    (2)Malloc是函数 ,new是关键字(可自己实现重载)

    (3)Malloc需要具体的字节;new只需要对象名

    (4)Newdelete会调用对象的构造和析构

    (5)返回值问题,malloc成功返回void*失败返回NULL new成功返回对象的指针,失败抛出异常(也支持返回NULL,为了兼容c,但一般不用)你们可以看看底层实现。

    Malloc扩容可以用rellocnew不行,若当前申请的内存获取不足时,new有一个set_new_handler机制,循环尝试压缩内存,若超出时间限制抛出异常,而malloc失败则没有这种机制,直接返回NULL 

    【2】new和new[]的区别?

      (1)new生成单个对象的空间,new[]生成数组

    【3】堆和栈的区别?

    1、管理方式不同;
    2、空间大小不同;
    3、能否产生碎片不同;
    4、生长方向不同;
    5、分配方式不同;
    6、分配效率不同;
    管理方式: 栈:系统开辟,系统释放

                      堆:手动开辟,手动释放

    空间大小:一般来讲在32位系统下,堆内存可以达到2G的空间,栈空间大小是1M。
    碎片问题:对于堆来讲,频繁的new/delete势必会造成内存空间的不连续,从而造成大量的碎片,使程序效率降低。对于栈来讲,则不会存在这个问题,因为栈是先进后出的队列,他们是如此的一一对应,以至于永远都不可能有一个内存块从栈中间弹出,在他弹出之前,在他上面的后进的栈内容已经被弹出

    生长方向:堆:生长方向向上,也就是向着内存地址增加的方向;

    栈:生长方向向下,是向着内存地址减小的方向增长。
    分配方式:堆:都是动态分配的,没有静态分配的堆

    栈有2种分配方式:静态分配和动态分配。静态分配是编译器完成的,比如局部变量的分配。动态分配由alloca函数进行分配,但是栈的动态分配和堆是不同的,他的动态分配是由编译器进行释放,无需我们手工实现。

    打开工程,依次操作菜单如下:Project->Setting->Link,在Category 中选中Output,然后在Reserve中设定堆栈的最大值和commit。
    注意:reserve最小值为4Byte;commit是保留在虚拟内存的页文件里面,它设置的较大会使栈开辟较大的值,可能增加内存的开销和启动时间。
    分配效率:栈是机器系统提供的数据结构,计算机会在底层对栈提供支持:分配专门的寄存器存放栈的地址,压栈出栈都有专门的指令执行,这就决定了栈的效率比较高。堆则是C/C++函数库提供的,它的机制是很复杂的,例如为了分配一块内存,库函数会按照一定的算法在堆内存中搜索可用的足够大小的空间,如果没有足够大小的空间(可能是由于内存碎片太多),就有可能调用系统功能去增加程序数据段的内存空间,这样就有机会分到足够大小的内存,然后进行返回。显然,堆的效率比栈要低得多。
        从这里我们可以看到,堆和栈相比,由于大量new/delete的使用,容易造成大量的内存碎片;由于没有专门的系统支持,效率很低;由于可能引发用户态和核心态的切换,内存的申请,代价变得更加昂贵。所以栈在程序中是应用最广泛的,就算是函数的调用也利用栈去完成,函数调用过程中的参数,返回地址,EBP和局部变量都采用栈的方式存放。

    【4】有哪些内存泄漏,内存非法访问的操作?怎么定位?怎么解决?

    free了空指针,free了两次相同内存,数组越界

    【5】看完用户空间的内存分配,那么内核是怎么做的?伙伴系统的原理是怎么样的?Slab分配器知道吗?

  • 相关阅读:
    C# ViewState
    ASP.NET C#中Application Session 的用法
    JS取 Input 控件值方法
    JS通过HTML标签自身属性获取属性值
    SQLServer 数据库操作
    查看和修改 Windows 实例远程桌面默认端口
    只能输入数字0-9 正则表达式,兼容Google Firefox IE浏览器
    C# 类初始化顺序
    window系统 任务计划程序
    微信公众号开发--消息接收与回复
  • 原文地址:https://www.cnblogs.com/xcb-1024day/p/11179125.html
Copyright © 2020-2023  润新知