iOS 内存管理(再探究)


概述: 结合内存分配和使用内存访问再谈内存管理(不适合内存管理小白)


一.为什么要管理内存?

  • 1.1 answer:

系统不帮我们管理,我们就自己管理啊!!!,听起来很对啊,可是系统为什么要提我们管理呢? 当然是为了以最优方式最大化利用有限的内存资源了!,那就有必要先来了解下内存分配算法了.

  • 1.2内存分配算法

    (不想看的直接看后面总结)

  • 首次适应算法

    使用该算法进行内存分配时,从空闲分区链首开始查找,直至找到一个能满足其大小需求的空闲分区为止。然后再按照作业的大小,从该分区中划出一块内存分配给请求者,余下的空闲分区仍留在空闲分区链中。
      该算法倾向于使用内存中低地址部分的空闲分区,在高地址部分的空闲分区非常少被利用,从而保留了高地址部分的大空闲区。显然为以后到达的大作业分配大的内存空间创造了条件。缺点在于低址部分不断被划分,留下许多难以利用、非常小的空闲区,而每次查找又都从低址部分开始,这无疑会增加查找的开销。

  • 循环首次适应算法

    该算法是由首次适应算法演变而成的。在为进程分配内存空间时,不再每次从链首开始查找,而是从上次找到的空闲分区开始查找,直至找到一个能满足需求的空闲分区,并从中划出一块来分给作业。该算法能使空闲中的内存分区分布得更加均匀,但将会缺乏大的空闲分区。

  • 最佳适应算法

    该算法总是把既能满足需求,又是最小的空闲分区分配给作业。为了加速查找,该算法需求将所有的空闲区按其大小排序后,以递增顺序形成一个空白链。这样每次找到的第一个满足需求的空闲区,必然是最优的。孤立地看,该算法似乎是最优的,但事实上并不一定。因为每次分配后剩余的空间一定是最小的,在存储器中将留下许多难以利用的小空闲区。同时每次分配后必须重新排序,这也带来了一定的开销。

  • 最差适应算法

    最差适应算法中,该算法按大小递减的顺序形成空闲区链,分配时直接从空闲区链的第一个空闲分区中分配(不能满足需要则不分配)。非常显然,如果第一个空闲分区不能满足,那么再没有空闲分区能满足需要。这种分配方法初看起来不太合理,但他也有非常强的直观吸引力:在大空闲区中放入程式后,剩下的空闲区常常也非常大,于是还能装下一个较大的新程式。
      最坏适应算法和最佳适应算法的排序正好相反,他的队列指针总是指向最大的空闲区,在进行分配时,总是从最大的空闲区开始查寻。
      该算法克服了最佳适应算法留下的许多小的碎片的不足,但保留大的空闲区的可能性减小了,而且空闲区回收也和最佳适应算法相同复杂。

总结:综合以上四种常见的内存分配算法,我们不看看出,要解决最大的问题就是如何最大化最快捷的利用内存,也就是我们内存管理的终极目的;

二. 内存访问方式:

简单说,CPU 就是通过内存的物理地址来确定数据所在的准确位置,然后进行读写操作;

(下面一段内容可直接跳过,主要是对上述访问过程的详细分析)

  • 2.1底层的访问流程如下:


  1. CPU 通过地址总线 确认要访问的内存单元号和具体的数据地址;
  2. CPU 通过控制总线告诉内存要进行的操作,read , write or other;
  3. CPU与内存通过数据总线进行数据传输;
    以上所有的数据传输,皆是以高(1)低(0)电平的形式进行,是不是已经很接近机器语言了?(很久没看硬件层的东西了,若有错误还望指正)

  • 2.2为什么要引入物理地址

    物理地址怎么来的呢?当然是逻辑地址映射而来的,为什么要引入逻辑地址?直接使用物理地址不是更好的节约CPU开销吗?下面来解释为什么:

    假设一台设备有2G内存,还要跑多个程序,还要接受消耗很大内存的程序,他是怎么做到的?搞计算机的人都是很聪明的(请允许我讲一个事实),在操作系统层面做了物理地址和逻辑地址之间的映射转换,当然处理器硬件上也做了支持。一个程序在运行时,实际要用到的指令和数据都是很有限的,不可能从头到尾同时用。那么对于一个程序来说,假装自己有非常大的空间,实际上只要有条理的把暂时要用到的部分放进物理内存供CPU访问就好。那既然每个程序(进程)只用一小块,那整个物理内存就可以分给多个程序(进程)用了。当然,这样做的前提是,数据和指令的动态进出,用完了的暂时不用的踢出内存,需要用的及时加载进来。很多实现方式是在外存中开了个交换区供换入换出,但iOS可略有不同.

  • 2.3 iOS 是如何应对内存不足的的

    首先,iOS和其它系统一样,内存分页,每页4K。多个页构成一个region统一管理,负责管理的对象是VM object,其中包含了pager、size、resident pages等诸多属性。
    但是 iOS的操作系统引入了沙盒机制,更是抛弃了不必要的复杂,在系统层面不支持App内存页换出。当内存吃紧时,对于可以重新载入的只读数据来说,直接清理掉,而对于可写的数据,只能通过App自己去管理维护。内存紧张时,iOS会向App发起memory warning,不配合释放足够内存者,杀!所以,iOS 始终保持着系统自我运行环境良好,并不十分关心具体应用哦;

开发中注意点1:memory warning 处理不当, APP 存在被被系统强制杀死的可能性


三. 内存分布:

想要管理内存,总得知道你要管理的数据在哪吧?看一个经典的C 内存分布:

内存分布.png

最简单来说分为两大部分:指令+数据。再细分一点,五部分:代码(机器码,看构造应该是ARM 指令集),初始化数据区,未初始化数据区,堆,栈。

  • 代码:(指令即机器码,看构造应该是ARM 指令集)就不用说了,最静态的,就是只读的东西;
  • 初始化数据,简单理解就是有初始值的变量、常量;
  • 未初始化数据,只声明未给值的变量,运行前统统为0,之所以单独分出来,估计是性能考虑;
  • 栈: 程序运行记录,每个线程,也就是每个执行序列各有一个(看crash log最容易理解),都是编译的时候能确定好的,还有一个特点就是这里面的数据可以不用指针,也不会丢;
  • 堆: 最灵活的内存区,用途多多,动态分配和释放,编译时不能提前确定,我们的Objective-C对象都是这么来的,都存在这里,通常堆中的对象都是以指针来访问的,指针从线程栈中来,但不独属于某个线程,堆也是对复杂的运行时处理的基础支持;

MRC 所说的“谁分配谁释放”说的都是堆上对象的管理;


总结: 通过内存分布,不难发现我们所要管理的基本就是堆 和 栈了!

四. 堆和栈的分析:

堆栈具体特性,百度大把,不做阐述,下面简述分配与访问:

  • 4.1 堆

  • 分配 : 执行时动态分配和释放,编译器不能确定具体值,此特性也奠定了OC 动态语言的基础,内存管理的主要区段;(引用类型存储区,也就是OC对象)

  • 访问:内存的分配原则(init 采用循环首次适应算法),导致了堆内有大量的碎片存在,有效数据区并非连续,通常通过栈区访问,由栈去的指针指向堆去的数据区,从而找到数据对应的位置;

  • 4.2 栈

  • 分配: 依次紧密排列,遵循先进后出(FILO)的原则,也就决定了其对小数据块读写相对较快;一般用作存储值类型数据(基本数据类型)

  • 访问: 通过逻辑地址映射为物理地址,由CPU通过总线进行数据访问;

上述分析涉及一个基本类型和OC对象存储的问题,基本数据类型在栈区,OC对象在堆区,那么如若对数据进行装箱和拆箱操作,势必会带来额外的内存开销,所以,不要不关心数据类型!

随带引出野指针和内存泄露的概念:

  • 野指针: 对应堆区数据已经释放,但是栈区指针的指针并未被清空;
  • 内存泄露: 对应栈区的指针已被清空,但其所指向的堆区内存并未释放;
  • 空指针: 没有对应的堆区,即没有指向的存储空间;

五. 内存相关的修饰符

  • strong :强引用,ARC中使用,与MRC中retain类似,使用之后,计数器+1。
  • weak :弱引用 ,ARC中使用,如果只想的对象被释放了,其指向nil,可以有效的避免野指针,其引用计数为1。
  • readwrite : 可读可写特性,需要生成getter方法和setter方法时使用。
  • readonly : 只读特性,只会生成getter方法 不会生成setter方法,不希望属性在类外改变。
  • assign :赋值特性,不涉及引用计数,弱引用,setter方法将传入参数赋值给实例变量,仅设置变量时使用。
  • retain :表示持有特性,setter方法将传入参数先保留,再赋值,传入参数的retaincount会+1。
  • copy :表示拷贝特性,setter方法将传入对象复制一份,需要完全一份新的变量时。
  • nonatomic :非原子操作,不加同步,多线程访问可提高性能,但是线程不安全的。决定编译器生成的setter getter是否是原子操作。
  • atomic :原子操作,同步的,表示多线程安全,与nonatomic相反。(所以,在并发读写的时候可以考虑直接使用atomic,而不是使用各种锁/信号量等)

六. 看完内存分配算法顺便分析一下 alloc 和 new 的区别:

alloc 和 new 最终都要调用底层的 malloc 函数族;
直接上源码:

  • new
+new{
id newObject = (*_alloc)((Class)self, 0);
Class metaClass = self->isa;
if (class_getVersion(metaClass) > 1)
    return ;
else
    return newObject;
}
  • alloc
 +alloc
{
return (*_zoneAlloc)((Class)self, 0, malloc_default_zone());
}

很明显,区别只在于alloc分配内存的时候使用了zone,这个zone是个什么东东呢?它是给对象分配内存的时候,把关联的对象分配到一个相邻的 内存区域内(循环首次适应算法 ),以便于调用时消耗很少的代价,提升了程序处理速度;

new 和 malloc 区别:

  • new 是c++中的操作符,malloc是c 中的一个函数
  • new 不止是分配内存,而且会调用类的构造函数,同理delete会调用类的析构函数,而malloc则只分配内存,不会进行初始化类成员的工作,同样free也不会调用析构函数
  • 内存泄漏对于malloc或者new都可以检查出来的,区别在于new可以指明是那个文件的那一行, 而malloc没有这些信息。
new 和 malloc效率比较
  • new可以认为是malloc加构造函数的执行。
  • new出来的指针是直接带类型信息的。
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 159,716评论 4 364
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 67,558评论 1 294
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 109,431评论 0 244
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 44,127评论 0 209
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 52,511评论 3 287
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 40,692评论 1 222
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 31,915评论 2 313
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 30,664评论 0 202
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 34,412评论 1 246
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 30,616评论 2 245
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 32,105评论 1 260
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 28,424评论 2 254
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 33,098评论 3 238
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 26,096评论 0 8
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 26,869评论 0 197
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 35,748评论 2 276
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 35,641评论 2 271

推荐阅读更多精彩内容