转载

栈 & 堆 |--> 内存管理

内存管理:

栈区 [ stack]: 由编译器 自动 分配并释放 ,一般存放函数的参数值,局部变量等

堆区 [ heap]: 程序员 分配和释放 ,如果程序员不释放,程序结束时,可能会由操作系统回收

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

文字常量区: 存放 常量字符串程序结束后由系统释放

程序代码区: 存放函数的 二进制代码

官方文档:

栈 & 堆 |--> 内存管理

iOS开发 中:

内存分析

1.  静态分析 (Analyze)

不运行程序 , 直接检测代码中是否有潜在的内存问题 ( 不一定百分百准确 , 仅仅是提供建议 )

结合实际情况来分析 , 是否真的有内存问题

2.  动态分析 (Profile == Instruments)

1 > 运行程序 , 通过使用 app, 查看内存的分配情况 ( Allocations )

* 可以查看做出了某个操作后 ( 比如点击了某个按钮 / 显示了某个控制器 ), 内存是否有暴增的情况 ( 突然变化 )

2 > 运行程序 , 通过使用 app, 查看是否有内存泄漏 ( Leaks )

* 红色区域代表内存泄漏出现的地方

内存使用注意

1.  加载小图片 / 使用频率比较高的图片

1 > 利用 imageNamed: 方法 加载过的图片 , 永远有缓存 , 这个缓存是由系统管理的 , 无法通过代码销毁缓存

2.  加载大图片 / 使用频率比较低的图片 ( 一次性的图片 , 比如版本新特性的图片 )

1 > 利用 initWithContentsOfFile: /imageWithContentsOfFile:/imageWithData: 等方法加载过的图片 , 没有缓存 , 只要用完了 , 就会自动销毁

2 > 基本上 , imageNamed: 方法以外 , 其他加载图片的方式 , 都没有缓存

PS:--关于图片资源

1 > 会压缩到 Assets.car 文件 , 没有直接暴露 在沙盒的资源包 (main Bundle)

* 条件 : " Deployment Target >= 7.x " 并且是 " 放在 Images.xcassets 里面的所有图片 "

* 影响 : 无法得到 图片的全路径 , 只能通过图片名 ( imageNamed: 方法 ) 来加载图片 , 永远会有缓存

2 > 不会压缩到 Assets.car 文件 , 直接暴 在沙盒的资源包 (main Bundle)

* 条件 : 1 > 以外的所有情况

* 影响 : 可以得到图片的 全路径 , 可以通过全路径 ( imageWithContentsOfFile: 方法 ) 来加载图片 , 不会有缓存

三. 如何让程序尽量减少内存泄漏

1.  ARC--MRC

* Foundation 对象 (OC 对象 ) :

只要方法中包含了 alloc/new/copy/mutableCopy/retain 等关键字 ,

那么这些方法产生的对象 , 就必须在不再使用的时候调用 1 release 或者 1 autorelease

* CoreFoundation 对象 (C 对象 ) :

只要函数中包含了 create/new/copy/retain 等关键字 ,

那么这些方法产生的对象 , 就必须在不再使用的时候调用 1 CFRelease 或者其他 release 函数

2.  ARC ( 只自动管理 OC 对象 , 不会自动管理 C 语言对象 )

* CoreFoundation 对象 (C 对象 ) :

只要函数中包含了 create/new/copy/retain 等关键字 , 那么这些方法产生的对象 , 就必须在不再使用的时候调用 1 CFRelease 或者其他 release 函数

* 利用 __bridge 桥接,告诉编译器 管理c语言对象。

3.  block 的注意 --见:block详解!

// block的内存默认在栈里面(系统自动管理)  void (^test)() = ^{  };
// 如果对block进行了Copy操作, block的内存会迁移到堆里面(需要通过代码管理内存)  Block_copy(test);
// 在不需要使用block的时候, 应该做1次release操作  Block_release(test);  [test release];

--------------------

:由系统自动分配 -- 系统自动分配 ,速度较快 [ FIFO ]

栈区结构:链表 or 数组

栈:栈是向低地址扩展的数据结构,是一块连续的内存的区域, 栈顶的地址和栈的最大容量是系统预先规定好的 iOS 中,主线程栈的大小是 1M

--[ 栈区:申请内存后响应 ]

栈:只要栈的剩余空间大于所申请空间,系统将为程序提供内存,否则将报异常提示栈溢出

-- 存储的内容

栈: 变量名 ( 不带 *) 相当于是指向栈区数据的指针别名,变量名可以简化程序员的工作

--------------------

:需要程序员自己申请, alloc copy -- 是由 alloc 分配的内存 ,速度比较慢,而且容易产生内存碎片,不过用起来最方便 [ LIFO ]

堆区结构:树 or 链表

堆:堆是向高地址扩展的数据结构,是不连续的内存区域。这是由于系统是 用链表来存储的空闲内存地址的 ,自然是不连续的,而链表的遍历方向是由低地址向高地址。堆的大小受限于计算机系统中有效的虚拟内存。由此可见,堆获得的空间比较灵活,也比较大

--[ 堆区:申请内存后响应 ]

堆:

1. 操作系统有一个 记录空闲内存地址 的链表

2. 当系统收到程序的申请时,会 遍历该链表 ,寻找第一个空间 大于 所申请空间的堆结点,然后将该结点从空闲结点链表中删除,并将该结点的空间分配给程序

3. 由于找到的堆结点的大小不一定正好等于申请的大小,系统 会自动的将多余的那部分重新放入空闲链表

-- 存储的内容

堆:要访问堆区中的数据,必须通过指针的方式才可以进行, 指针的类型决定了访问堆中数据的方式

--堆区的操作!

1、操作系统 以匿名 ( 只记录内存地址和大小,不记录具体类型 ) 的方式 记录已经分配的内存区域

2、当某一内存区域不再使用时, 程序需要通知操作系统回收该内存区域 ,从而可以保证该内存区域被其他程序再次使用, 否则, 该区域将永远无法被再次分配 ,这就是传说中的 内存泄露

3、如果某一区域 已经被释放仍然视图访问 该区域,会提示 坏内存访问 ,这就是传说中的 野指针访问

--------------------

总结:

1、我们 程序员 只需要关心 堆区 的内存管理,而栈区系统会自动回收。

2、栈区存放的局部变量, 出了作用域就 会被销毁!

3、内存结构中的其他几个分区, 跟程序同生死 :程序启动时,被加载到内存中;程序摧毁,内存被回收!

PS:在 iOS开发 中:

1> 堆区的内存 是所有应用程序 共享 的,问题:内存泄露 & 野指针

|--> 内存泄漏 :如果程序员使用变量后, 不释放内存 (有强指针指向它),该内存区域会永远被占有,别的Application无法使用,即 内存警告

|--> 内存溢出 (Out Of Memory)内存不够使用 了, 数据长度 比较小 的数据类型 存储了 数据长度 比较大 的数据类型

|--> 野指针 :当 对象已经释放 ,程序中变量的 指针依然指向 该内存地址,如果再向该对象发送消息,就会 报“野指针”错误

2> 堆区的 内存分配 是由系统负责的。

3> 系统是使用 一个链表 来维护所有已经 分配过的内存空间 。(见:上边--堆区:申请内存后响应)

4> 系统只是记录分配了多少字节给应用程序,并不管理具体的类型-->“ 匿名 ”:只是记录分配的内存大小,不管具体是哪个类的!

5> 如果变量使用结束后,需要释放内存,OC中当一个变量的 引用计数==0 时,说明没有变量使用这块内存空间,即 系统直接回收

-------

分享:

数据结构&算法分析 PDF,下载: Download  

-----------

|--> Copyright (c) 2015 Bing Ma.

|--> GitHub RUL:  https://github.com/SpongeBob-GitHub

正文到此结束
Loading...