美文网首页
iOS底层探索32、内存管理

iOS底层探索32、内存管理

作者: _zhang__ | 来源:发表于2020-12-05 23:44 被阅读0次

本文开始对内存管理进行探究分析。

一、内存布局

image.png

内存布局图中,
高地址0xc0000000,针对4G的内存,0xc0000000为3G的位置: 0xc0000000 --> 十进制 --> ÷1024(B) ÷1024(M) ÷1024(G) = 3G
低地址0x00400000,不从0x00000000开始而是留有保留区的原因:
0x00000000 其代表 nil/,保留段一般留给系统处理例如 nil、底层内核发送信号的一些指针地址等。

内存五大区 测试Demo:

相关分析以添加在注释中,不再多做赘述。

@implementation ViewController

int clA;
int clB = 10;

static int bssA;
static NSString *bssStr1;

static int bssB = 10;
static NSString *bssStr2 = @"memorytest";
static NSString *myname = @"Domy";


- (void)viewDidLoad {
    [super viewDidLoad];
    
    [self testStack];
    [self testHeap];
    [self testConst];
}

- (void)testStack {
    NSLog(@"************栈区************");
    // 栈区
    int a = 10;
    int b = 20;
    NSObject *object = [NSObject new];
    NSLog(@"a == \t%p",&a);
    NSLog(@"b == \t%p",&b);
    NSLog(@"object == \t%p",&object);
    NSLog(@"%lu",sizeof(&object));
    NSLog(@"%lu",sizeof(a));
}

- (void)testHeap {
    NSLog(@"************堆区************");
    // 堆区
    NSObject *object1 = [NSObject new];
    NSObject *object2 = [NSObject new];
    NSObject *object3 = [NSObject new];
    NSObject *object4 = [NSObject new];
    NSObject *object5 = [NSObject new];
    NSObject *object6 = [NSObject new];
    NSObject *object7 = [NSObject new];
    NSObject *object8 = [NSObject new];
    NSObject *object9 = [NSObject new];
    NSLog(@"object1 = %@",object1);
    NSLog(@"object2 = %@",object2);
    NSLog(@"object3 = %@",object3);
    NSLog(@"object4 = %@",object4);
    NSLog(@"object5 = %@",object5);
    NSLog(@"object6 = %@",object6);
    NSLog(@"object7 = %@",object7);
    NSLog(@"object8 = %@",object8);
    NSLog(@"object9 = %@",object9);
    // 访问---通过对象->堆区地址->存在栈区的指针
}

- (void)testConst {
    
    // 问题1: 全局变量和局部变量在内存中是否有区别?如果有,是什么区别?
    /**
    全局变量 在全局区,生命周期在整个文件
    局部变量 所在位置位置不定,生命周期局部函数作用域内
    */
    char charA[] = {'a','a','a','a','a','a','a','a','a','a','a'};
    NSLog(@"charA:%p",charA);// 栈 charA:0x7ffeeb9654cd
    char charB;
    NSLog(@"&charB:%p",&charB);// 栈 charB:0x7ffee8eef4b7
    // 静态区 strA:0x108b4f2f8
    NSString *strA = @"张三李四王五";
    NSLog(@"strA:%p",strA);


    // 问题2: Block 是否可以直接修改全局变量? --> 是的,全局变量不进行捕获
//    [UIView animateWithDuration:1 animations:^{
//        myname = @"zhang";
//    }];
//    NSLog(@"myname = %@",myname);// zhang
    
    
    NSLog(@"************静态区************");
    NSLog(@"clA == \t%p",&clA);
    NSLog(@"bssA == \t%p",&bssA);
    NSLog(@"bssStr1 == \t%p",&bssStr1);
    
    NSLog(@"************常量区************");
    NSLog(@"clB == \t%p",&clB);
    NSLog(@"bssB == \t%p",&bssB);
    NSLog(@"bssStr2 == \t%p",&bssStr2);
    

    NSLog(@"************静态区安全测试************");
    // 100 可以修改
    // 只针对文件有效 -
    NSLog(@"vc:%p--%d",&personNum,personNum); // 100
    personNum = 10000;
    NSLog(@"vc:%p--%d",&personNum,personNum); // 10000
    [[MyPerson new] run]; // 100 + 1 = 101
    NSLog(@"vc:%p--%d",&personNum,personNum); // 10000
    [MyPerson eat]; // 102
    NSLog(@"vc:%p--%d",&personNum,personNum); // 10000
   
    [[MyPerson alloc] cate_method];

    
    // 扩展
    MyPerson *p = [MyPerson new];
    p.name = @"name";
    NSLog(@"对象 p:%p -- &p:%p",p,&p);
    /**
     对象p: 0x600001094620  --  对象p 在堆区
     &p:      0x7ffee67374d8    --  指针 在栈区
     */
    // p.name:0x10b0ac400 -- 常量区
    NSLog(@"p.name:%p",p.name);
}

@end

运行工程,输出打印:

五大区Demo[18362:1527836] ************栈区************
五大区Demo[18362:1527836] a ==     0x7ffee58434cc
五大区Demo[18362:1527836] b ==     0x7ffee58434c8
五大区Demo[18362:1527836] object ==    0x7ffee58434c0
五大区Demo[18362:1527836] 8
五大区Demo[18362:1527836] 4
五大区Demo[18362:1527836] ************堆区************
五大区Demo[18362:1527836] object1 = <NSObject: 0x600001004810>
五大区Demo[18362:1527836] object2 = <NSObject: 0x600001004830>
五大区Demo[18362:1527836] object3 = <NSObject: 0x600001004820>
五大区Demo[18362:1527836] object4 = <NSObject: 0x600001004840>
五大区Demo[18362:1527836] object5 = <NSObject: 0x600001004850>
五大区Demo[18362:1527836] object6 = <NSObject: 0x600001004860>
五大区Demo[18362:1527836] object7 = <NSObject: 0x600001004880>
五大区Demo[18362:1527836] object8 = <NSObject: 0x600001004890>
五大区Demo[18362:1527836] object9 = <NSObject: 0x6000010048a0>
五大区Demo[18362:1527836]charA:0x7ffeede744cd
五大区Demo[18456:1536336] &charB:0x7ffeede744b7
五大区Demo[18456:1536336] strA:0x101d8b318
五大区Demo[18362:1527836] ************静态区************
五大区Demo[18362:1527836] clA ==   0x10a3bd524
五大区Demo[18362:1527836] bssA ==  0x10a3bd528
五大区Demo[18362:1527836] bssStr1 ==   0x10a3bd530
五大区Demo[18362:1527836] ************常量区************
五大区Demo[18362:1527836] clB ==   0x10a3bd508
五大区Demo[18362:1527836] bssB ==  0x10a3bd518
五大区Demo[18362:1527836] bssStr2 ==   0x10a3bd510
五大区Demo[18362:1527836] ************静态区安全测试************
五大区Demo[18362:1527836] vc:0x10a3bd51c--100
五大区Demo[18362:1527836] vc:0x10a3bd51c--10000
五大区Demo[18362:1527836] MyPerson内部:-0x10a3bd500--101
五大区Demo[18362:1527836] vc:0x10a3bd51c--10000
五大区Demo[18362:1527836] MyPerson内部:MyPerson-0x10a3bd500--102
五大区Demo[18362:1527836] vc:0x10a3bd51c--10000
五大区Demo[18362:1527836] MyPerson内部:-0x10a3bd520--100
五大区Demo[18362:1527836] 对象 p:0x600001008470 -- &p:0x7ffee58434c8
五大区Demo[18362:1527836] p.name:0x10a3bc400

二、内存管理策略

1、TaggedPointer - 小对象类型

NSString *str = @"";
0xa000000000000621 
 --> 高位 0xa --> 1  010 --> 2   
 --> 低位 621 -->   

int a = ;    
0xb000000000000025 
 --> 高位 0xb --> 1  011 --> 3
 --> 低位 25   --> 
如上,最高位为1,作为标志是否是taggedPointer类型;

2 : string 类型 
3 : int 类型    

TaggedPointer 类型的变量,在iOS14以后,其地址做了一步编码混淆(异或某个值),解码时只要再次异或即可。

TaggedPointer 类型的变量总结:

  1. 指针的值包含:地址 + 值
  2. 值的操作不会进行 retain/release,因此其内存接由系统管理释放回收;
  3. 直接在内存的静态区读取,比普通对象内存读取效率高3倍,创建速度高106倍 (来自WWDC官方公布数据)。

2、引用计数

retain/release 分析

补充,retain操作时,针对 nonpointer类型的ISA,对象是通过散列表进行存取。sideTables 并非所有类都在一个散列表中,但也并非每个类都有自己的一张散列表。通过源码可看到,sideTables 在真机非模拟器上最多可以创建8 张,其他可创建 64 张。

待续......

相关文章

网友评论

      本文标题:iOS底层探索32、内存管理

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