前言
内存管理是婚恋交友系统源码开发过程中不可忽视的部分,出现的很多问题都和内存有关。我们都知道内存的五大区,那么它是怎样布局的,接下来本将对它进行讲解。
内存分区与布局
- 以
4G
手机为例的内存五大区与布局
如图所示:
栈区(Stack)
- 栈区是一块
连续的
内存空间,它的结构是从高地址往低地址
拉伸,遵循先进后出(FILO)
原则 - 栈区存储的是
局部变量
,函数
,方法
,参数
,指针
- 栈区的地址空间一般是以
0x7
开头 - 栈区由
编译器
自动分配内存和释放
举例:
- (void)testStack{
// 栈区
int a = 10;
int b = 20;
NSObject *object = [NSObject new];
NSLog(@"a == %p",&a);
NSLog(@"b == %p",&b);
NSLog(@"object == %p",&object);
NSLog(@"%lu",sizeof(&object));
NSLog(@"%lu",sizeof(a));
}
打印结果如下:
- 可以观察出栈区内存连续,且内存是从高地址往低地址扩展,内存是
0x7
开头
堆区(Heap)
- 堆区是一块
不连续
的内存空间,它的结构是从低地址向高地址
扩展 - 堆区存储的是
对象
,需要开辟空间
的东西,栈区内存比较小,堆区比较大,所以在堆区开辟空间 - 堆区地址空间一般以
0x6
开头
举例:
- (void)testHeap{
// 堆区
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);
}
结果如下:
- 从打印结果中可以看出来堆区的内存是不连续的,且是
0x6开头
全局区(静态区)
- 全局区又分为
.bss段
和.data段
,内存地址一般由0x1
开头:
-
Bss
段:未初始化
的全局变量,静态变量, -
Data
段:已初始化
的全局变量,静态变量
- 举例:
- (void)globalTest {
// 全局区
NSLog(@"************ bss ************");
NSLog(@"bssA == \t%p",&bssA);
NSLog(@"bssB == \t%p",&bssB);
NSLog(@"bssStr == \t%p",&bssStr);
NSLog(@"************ data ************");
NSLog(@"dataA == \t%p",&dataA);
NSLog(@"dataB == \t%p",&dataB);
NSLog(@"dataStr == \t%p",&dataStr);
}
- 运行结果如下:
静态安全测试
- 经常有人说静态区也称作
静态安全区
,下面我们就来验证它为什么安全,创建一个WSPerson
类,以及它的分类:
// WSPerson.h
static int ws_number = 100;
@interface WSPerson : NSObject
- (void)eat;
+ (void)sleep;
@end
// WSPerson.m
- (void)eat {
ws_number++;
NSLog(@"%s __ %p __ %d", __func__, &ws_number, ws_number);
}
+ (void)sleep {
ws_number++;
NSLog(@"%s __ %p __ %d", __func__, &ws_number, ws_number);
}
// WSPerson+App.h
@interface WSPerson (App)
- (void)cate_test;
@end
// WSPerson+App.m
- (void)cate_test {
ws_number++;
NSLog(@"%s __ %p __ %d", __func__, &ws_number, ws_number);
}
在ViewController
中的调用代码如下:
- (void)constTest {
[[WSPerson alloc] eat];
NSLog(@"%s __ %p __ %d", __func__, &ws_number, ws_number);
ws_number = 10000;
NSLog(@"%s __ %p __ %d", __func__, &ws_number, ws_number);
[[WSPerson alloc] eat];
NSLog(@"%s __ %p __ %d", __func__, &ws_number, ws_number);
[WSPerson sleep];
NSLog(@"%s __ %p __ %d", __func__, &ws_number, ws_number);
[[WSPerson alloc] cate_test];
NSLog(@"%s __ %p __ %d", __func__, &ws_number, ws_number);
}
打印出的结果比较有意思:
三个文件ws_number
的内存地址不一样,具体是原因需要借助Clang
查看三个文件的C++
代码:
从婚恋交友系统源码中可以观察到每个文件都有个ws_number
的全局变量,且初始值都一样。也就是说在多个文件使用同一个静态变量,系统会各自生成一个相同初始值
且地址不同
的静态变量,这样在各自文件内使用就不会互相干扰,数据比较安全。
常量区(.rodata)
- 常量区的内存在
编译时
就已经确定,主要存放已经使用过的,且没有指向的字符串常量
(因为字符串常量可能在程序中多次被使用,所以在程序运行之前
就会提前分配内存)。常量区的常量在程序结束后,由系统释放
代码区(.text)
- 存储程序代码,在编译时加载到内存中,代码会被编译成
二进制的形式
进行存储
其他
- 我们可以从图中看出,栈底的内存为
0c0000000
,转成10进制
后等于3GB
,还有1G
的内存分配给了内核区
。
-
内核区
:主要进行消息处理
以及多线程的操作
- 代码区的地址是从
0x00400000
开始,怎么不是从0x0
开始? 因为0x0
是nil
,从nil
开始分配会出现问题,所以在代码区之前就有了保留区
-
保留区
:预留给系统处理nil
等
以上就是婚恋交友系统源码的内存分区和布局的内容了。