本文开始对内存管理
进行探究分析。
一、内存布局
内存布局图中,
高地址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
类型的变量总结:
- 指针的值包含:
地址 + 值
; - 值的操作不会进行
retain/release
,因此其内存接由系统管理释放回收; - 直接在内存的静态区读取,比普通对象内存读取效率高3倍,创建速度高106倍 (来自WWDC官方公布数据)。
2、引用计数
补充,retain
操作时,针对 nonpointer
类型的ISA
,对象是通过散列表进行存取。sideTables
并非所有类都在一个散列表中,但也并非每个类都有自己的一张散列表。通过源码可看到,sideTables
在真机非模拟器上最多可以创建8 张
,其他可创建 64 张。
待续......