前言

内存管理是婚恋交友系统源码开发过程中不可忽视的部分,出现的很多问题都和内存有关。我们都知道内存的五大区,那么它是怎样布局的,接下来本将对它进行讲解。

内存分区与布局

  • 4G手机为例的内存五大区与布局如图所示:

婚恋交友 java 源码 婚恋系统源码_静态变量

栈区(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));
}

打印结果如下:

婚恋交友 java 源码 婚恋系统源码_婚恋交友 java 源码_02

  • 可以观察出栈区内存连续,且内存是从高地址往低地址扩展,内存是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);
}

结果如下:

婚恋交友 java 源码 婚恋系统源码_App_03

  • 从打印结果中可以看出来堆区的内存是不连续的,且是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);
}

打印出的结果比较有意思:

婚恋交友 java 源码 婚恋系统源码_静态变量_04

三个文件ws_number的内存地址不一样,具体是原因需要借助Clang查看三个文件的C++代码:

婚恋交友 java 源码 婚恋系统源码_ios_05

从婚恋交友系统源码中可以观察到每个文件都有个ws_number的全局变量,且初始值都一样。也就是说在多个文件使用同一个静态变量,系统会各自生成一个相同初始值地址不同的静态变量,这样在各自文件内使用就不会互相干扰,数据比较安全。

常量区(.rodata)

  • 常量区的内存在编译时就已经确定,主要存放已经使用过的,且没有指向的字符串常量(因为字符串常量可能在程序中多次被使用,所以在程序运行之前就会提前分配内存)。常量区的常量在程序结束后,由系统释放

代码区(.text)

  • 存储程序代码,在编译时加载到内存中,代码会被编译成二进制的形式进行存储

其他

  1. 我们可以从图中看出,栈底的内存为0c0000000,转成10进制后等于3GB,还有1G的内存分配给了内核区
  • 内核区:主要进行消息处理以及多线程的操作
  1. 代码区的地址是从0x00400000开始,怎么不是从0x0开始? 因为0x0nil,从nil开始分配会出现问题,所以在代码区之前就有了保留区
  • 保留区:预留给系统处理nil

以上就是婚恋交友系统源码的内存分区和布局的内容了。