美文网首页
iOS中堆和栈的

iOS中堆和栈的

作者: 54番茄 | 来源:发表于2018-03-13 10:43 被阅读21次


    栈的地址由高向低,堆的地址由低向高

    1.栈区(stack):

    存放函数的参数值、局部变量的值等,由编译器自动分配释放,通常在函数执行结束后就释放了,其操作方式类似数据结构中的栈。栈内存分配运算内置于处理器的指令集,效率很高,但是分配的内存容量有限,比如iOS中栈区的大小是2M。

    2.堆区(heap):

    就是通过new、malloc、realloc分配的内存块,它们的释放编译器不去管,由我们的应用程序去释放。如果应用程序没有释放掉,操作系统会自动回收。分配方式类似于链表。

    3.静态区:

    全局变量和静态变量的存储是放在一块的,初始化的全局变量和静态变量在一块区域,未初始化的全局变量和未初始化的静态变量在相邻的另一块区域。程序结束后,由系统释放。

    4.常量区:

    常量存储在这里,不允许修改的,如数字或字符, 程序退出后自动释放 。

    5.代码区

    存放函数体的二进制代码。

    总结:

    > 在函数体中定义的自动变量通常是在栈上
    > 用malloc, calloc, realloc等分配内存的函数分配得到的就是在堆上
    > 加了static修饰符后不管在哪里都存放在全局区(静态存储区)。
    在所有函数体外定义的是全局变量,存储在全局区(静态存储区)。
    > 在所有函数体外定义的static全局变量存储在全局区(静态存储区)。
    > 在函数体内定义的static静态变量存储在全局区(静态存储区),表示只在该函数体内有效,但是其生命周期却
        变为和  整个程序同生同死

    比如:函数中的"adgfdf"这样的字符串存放在常量区

    举例:
    一个网上经典的例子:

    int a = 0; 全局初始化区
    char *p1; 全局未初始化区

    main(){        

        int b;                                   栈
        char s[] = "abc";                 栈
        char *p2;                            栈
        char *p3 = "123456";        123456在常量区,p3在栈上。
        static int c =0;                 全局(静态)初始化区
        p1 = (char *)malloc(10);
        p2 = (char *)malloc(20);     分配得来得10和20字节的区域就在堆区。
        strcpy(p1, "123456");         123456放在常量区,编译器可能会将它与p3所指向的"123456"优化成一个地方。
        
        NSString * string = @"你好"   你好"在常量区,string在栈上。


    }


    栈区和堆区的区别主要为以下几点:

    1.对于栈来说,内存管理由编译器自动分配释放;对于堆来说,释放工作由程序员控制。

    2.栈的空间大小(2M)比堆小许多。

    3.栈内存的更新速度要快于堆内存,因为局部变量的生命周期很短,所以分配效率比堆高。

    4.栈中存储的变量出了作用域就会被释放,而堆由于是由程序员进行控制释放的(ARC下堆内存存放的实体会被垃圾回收机制不定时的回收),变量的生命周期可以延长。

    栈对象严格的定义了生命周期也是其主要的缺点,栈对象的生命周期不适于Objective-C的引用计数内存管理方法。在objective-c中只支持一个类型对象:blocks。

    关于在block中的对象的生命周期问题。出现这问题的原因是,block是新的对象,当你使用block时候,如果你想对其保持引用,你需要对其进行copy操作,(从栈上copy到堆中,并返回一个指向他的指针),而不是对其进行retain操作。这就是为什么申明一个block要用copy的原因。

    扩展:

    1.申请释放方式

       在申请内存和释放内存方式方面,堆和栈有着很大的不同,先来谈谈栈是如何实现的吧。栈是编译器自动申请的,例如在主函数里面,要声明一个int变量a,那么编译器就自动开辟一块内存存放变量a。而堆则不相同,是由程序员手动申请的,只要程序员感觉程序此处需要用到多大的内存空间,那么就使用malloc或者new来申请固定大小的内存使用。栈的空间在程序结束的时候由系统或者编译器自动释放,而堆则在程序结束前由程序员手动使用delete释放,或者忘记手动释放,由系统在程序结束的时候自动回收。

    2.申请后系统的相应

    栈,只要栈剩余的空间大小比申请的空间小,系统就自动为其分配空间,否则就会报错说明栈空间溢出。

    堆,首先要知道操作系统中有一个存放空闲存储块的链表,当程序员申请空间的时候,系统就会遍历整个链表,找到第一个比申请空间大的空闲块节点,系统会将该空闲块从空闲链表中删除,分配给程序,同时系统会记录这个空闲块的首地址和申请的大小,当程序员使用delete释放该空间的时候能够找到该存储区。另外,申请的空间不一定与找到的空闲块大小相同,多出来剩余的空闲区会被系统重新添加到空闲链表中。

    3.申请的限制

    栈,是一种向低地址扩展的数据结构,并且是连续的存储空间,所以栈顶和栈的最大容量是固定的,在windows下,栈的最大容量是2m或者是1m,是在编译的时候就已经确定的,当申请空间大于栈的剩余空间的时候,就会报错说明overflow,所以栈能够申请的空间是比较有限的。

    堆,是一种向高地址扩展的数据结构,并且是不连续的,因为系统采用的是链表的方式存放空闲存储块,当然是不连续的,链表的遍历方向是由低向高的,所以堆能够申请的空间的大小其实等同于整个系统的虚拟内存,只要还有内存空间,那么堆就能够不受限制的申请空间,这种方式比较灵活,申请空间也较大。

    4.申请效率的比较

    栈,因为栈空间的申请是由系统自动完成的,所以速度快,但是不受程序员控制。

    堆,空间的申请是由malloc或new来完成的,实现起来较慢,能够产生碎片,但是使用起来方便。

    5.存放内容

    栈,栈存放的内容,一般来说是函数地址和相关参数。当主函数要调用一个函数的时候,要对当前断点进行保存,需要使用栈来实现,首先入栈的是主函数下一条语句的地址,然后是调用函数的参数,一般情况下是按照从右向左的顺序入栈,之后是调用函数的局部变量,注意静态变量是存放在全局内存区,是不入栈的;出栈的顺序正好相反,最终栈顶指向主函数下一条语句的地址,主程序又从该地址开始执行。

    堆,一般情况堆顶使用一个字节的空间来存放堆的大小,而堆中具体存放内容是由程序员来完成的。

    栈中保存指针地址,指针指向堆内存

    相关文章

      网友评论

          本文标题:iOS中堆和栈的

          本文链接:https://www.haomeiwen.com/subject/qqcofftx.html