美文网首页
iOS面试之内存管理模块

iOS面试之内存管理模块

作者: 木子心语 | 来源:发表于2019-11-24 18:56 被阅读0次

    内存管理内容如下:

    • 内存布局
    • 内存管理
    • 数据结构
    • ARC/MRC
    • 引用计数
    • 弱引用
    • 自动释放池
    • 循环引用
    内存管理.png

    1.内存布局

    内存布局.png
    - 最上方是内核区的内存,最下方是保留的内存空间
    - 中间是程序加载的内存空间
    - 由下到上是低地址到高地址
    - 程序加载到内存会分为三段,1.未初始化数据(.bss),2.已初始化数据(.data),3.代码段(.text)
    - 代码段:所写的代码在代码段内存中
    - 已初始化数据,静态变量,全局变量,已经初始化
    - 未初始化数据,静态变量,全局变量,未初始化
    - 栈,我们定义的方法或者函数都在栈中工作的
    - 栈从高地址向低地址进行扩展
    - 创建的对象,block经过copy放在堆上工作
    - 堆是向上增长
    
    • stack:(栈区):方法调用
    • heap:通过alloc等分配的对象
    • bss:未出化的全局变量等
    • data:已初始化的全局变量等
    • text:程序代码

    2.内存管理

    - TaggedPointer
    - NONPOINTER_ISA
    - 散列表
    
    • NONPOINTER_ISA
    64位架构,非指针类型
    
    NONPOINTER_ISA 1.png
    0-15位
    - indexed:如果是0,代表当前对象的类对象地址;如果是1,不仅是类对象的
    地址,还有内存管理方面的数据,非指针型的isa
    - has_assoc :当前对象是否有关联对象.如果是0,代表没有,1代表有.
    - has_cxx_dtor 是否使用到c++
    - shiftcls: 当前对象的类对象的指针地址
    
    NONPOINTER_ISA 2.png
    16-31位
    
    NONPOINTER_ISA 3.png
    32-47位
    - magic
    - weakly_referenced 弱引用指针
    - deallocating 当前是否正在dealloc操作
    - has_sidetable_rc 当前isa指针当中,存储的引用计数已经达到了上线的
    话,需要外挂一个sidetable_rc的数据结构,去存储相关的引用计数内容
    - extra_rc 额外的引用计数,当我们引用计数在很小的方位内,存在isa指针当中
    
    NONPOINTER_ISA4.png
    48-63位
    
    • 散列表
    SideTables()结构
    
    SideTables.png
    SideTables其实是一个哈希表
    
    SideTables()结构.png
    SideTable:
    - spinlock_t 自旋锁
    - RefcountMap 引用计数表
    - weak_table_t 弱引用表
    

    思考- 为什么不是一个SideTable?而是多个SideTable

    思考- 怎样实现快速分流?

    SideTables的本质是一张Hash表
    
    对象指针(Key) - > (Hash函数) -> SideTable(Value)
    
    • Hash查找
    Hash.png
    给定值是对象内存地址,目标值是数组下标索引
    
    f(ptr) = (uintptr_t)ptr % array.count
    
    通过hash查找,提高效率
    

    3.数据结构

    • 自旋锁
    - 忙等的锁
    - 轻量访问
    
    • 引用计数表


      引用计数表.png
    - hash表
    - 通过指针,可以找到对应对象的引用计数
    - 传入对象伪装操作,获取对应的引用计数
    - 存储一个对象的应用计数,通过DisguisedPtr函数计算存储位置
    - 获取对象引用计数值得时候,仍是通过DisguisedPtr函数计算索引的位置
    - 插入与获取都是通过一个同一个函数获取位置,避免了循环遍历
    - hash查找可以提高查找效率
    

    思考- 引用计数表是通过什么实现?Hash表

    • 弱引用表
    weak_table_t也是一张哈希表
    
    弱引用表.png
    - 对象指针,通过Hash函数,计算对应弱引用的对象存储位置
    - weak_entry_t是一个结构体数组
    

    4.ARC/MRC

    • MRC
    - 手动引用计数
    - alloc retain release
    - retainCount autorelease dealloc
    
    • ARC
    - 自动引用计数
    - 编译器与runtime协作的结果
    - 禁止手动调用retain/release/retainCount/dealloc
    - 新增weak,strong属性关键字
    

    5.引用计数管理

    • 实现原理分析
    alloc实现
    - 经过一系列调用,最终调用了C函数calloc
    - 此时并没有设置引用计数为1
    
    retiain实现
    SideTable & table = SideTables()[this];
    size_t & refcntStorage = table.refcnts[this];
    refcntStorage += SIDE_TABLE_RC_ONE;
    

    思考,retain操作,系统怎样查找引用计数的?经过两次hash查找

    release实现
    SideTable& table = SideTables()[this];
    RefcountMap::iterator it = table.refcnts.find(this);
    it->second -= SIDE_TABLE_RC_ONE;
    
    retainCount实现
    SideTable& table = SideTables()[this];
    size_t refcnt_result = 1;
    RefcountMap::iterator it = table.refcnts.find(this);
    refcnt_result += it->second >> SIDE-TABLE_RC_SHIFT;
    
    dealloc实现
    
    dealloc实现流程.png
    object_dispose()实现
    
    object_dispose()实现.png
    objc_destructInstance()实现
    
    objc_destructInstance()实现.png
    clearDeallocating()实现
    
    clearDeallocating()实现.png

    6.弱引用

    {
       id __weak obj_new = obj;
    }
    |
    编译
    |
    {
       id obj_new;
       objc_initWeak(&obj_new,obj);
    }
    
    objc_initWeak()
    |
    storeWeak()
    |
    weak_register_no_lock()
    
    
    清除weak变量,同时设置指向为nil
    dealloc---...--- weak_clear_no_lock()
    

    7.自动释放池

    • AutoreleasePool
    编译器将@autoreleasepool{}改写为:
    void *ctx = objc_autoreleasePoolPush();
    {}中的代码
    objc_autoreleasePoolPop(ctx);
    
    • objc_autoreleasePoolPush
    void *objc_autoreleasePoolPush(void)
    |
    void *AutoreleasePoolPage::push(void)
    
    • objc_autoreleasePoolPop
    void objc_autoreleasePoolPop(void* ctxt)
    |
    AutoreleasePoolPage::pop(void* ctxt)
    
    一次pop实际上一次批量的pop操作
    
    • 自动释放池数据结构
    - 以栈为结点通过双向链表的形式组合而成
    - 和线程一一对应
    
    • 双向链表
    双向链表.png
    栈结构.png
    - 高地址指向低地址
    - 出栈与入栈
    
    思考
    - AutoreleasePool的实现原理是怎样的?
    - AutoreleasePool为何可以嵌套使用?
    
    应用场景:在for循环中alloc图片数据等内存消耗较大的场景手动插入autoreleasepool
    

    8.循环引用

    - 自循环引用
    - 相互循环引用
    - 多循环引用
    
    • 自循环引用


      自循环引用.png
    - 有一个对象,对象中有一个成员变量obj,强持有这个成员变量,此成员变
    量赋值为元对象,就造成了自循环引用
    
    • 相互循环引用


      相互循环引用.png
    - 对象A,obj
    - 对象B,obj
    - 对象A中的obj指向B
    - 对象B中的obj指向A
    - 此时造成相互循环引用
    
    • 多循环引用


      多循环引用.png
    每一个对象的obj,都指向下一个对象,就产生了多循环引用
    
    思考-如何解决循环引用?
    - 避免产生循环引用
    - 在合适的时机手动断开循环引用
    
    具体的解决方案有哪些?
    _ _ weak
    对象A: id _ _ weak obj
    对象B: id _ _ strong obj
    对象B强持有A,对象A弱引用B
    
    _ _ block
    MRC下,_ _block修饰对象不会增加其引用计数,避免了循环引用
    ARC下,_ _block修饰对象会被强引用,无法避免循环引用,需手动解除循环引用
    
    _ _ unsafe_unretained
    修饰对象不会增加其引用计数,避免了循环引用
    如果被修饰对象在某一时机被释放,会产生悬垂指针
    

    思考-面试题

    • 什么是ARC?
    • 为什么weak指针指向的对象在废弃之后会被自动置为nil?
    • 如何实现AutoreleasePool?
    • 循环引用?你遇到过哪些循环引用?你是如何解决的?

    QQ交流群: 796142709

    相关文章

      网友评论

          本文标题:iOS面试之内存管理模块

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