区块链技术博客
www.b2bchain.cn

iOS的内存分布探究求职学习资料

本文介绍了iOS的内存分布探究求职学习资料,有助于帮助完成毕业设计以及求职,是一篇很好的资料。

对技术面试,学习经验等有一些体会,在此分享。

前言

最近遇到一些内存相关crash,排查问题过程中产生对进程内整个地址空间分布的疑惑。搜查了一番资料,网上关于Linux进程地址空间分布的介绍比较详细,但是iOS实际运行效果的比较少。
本文基于网上相关文章,进行实际测试,探究App实际运行过程中的地址分布。

正文

32位的分布情况

32位的机器,每个进程会有4G虚拟地址空间,较高的1G是从0xC0000000到0xFFFFFFFF的内核空间(Kernel Space ),较低的3G是从0x00000000到0xBFFFFFFF用户空间(User Space )。 内核空间中存放的是内核代码和数据,用户空间中存放的是App进程的代码和数据。这里地址指的都是虚拟地址空间,由操作系统负责映射为物理地址。
把最常用的几个概念堆、栈、数据段、代码段做一个地址从大到小的排序:
iOS的内存分布探究

  • 栈:在函数调用过程中,每个函数都会有一个相关的区域来存储函数的参数和局部变量,每次进行函数调用的时候系统都会往栈压入一个新的栈帧,在函数返回时清除。入栈和出栈的操作非常快,这个过程会用到两个寄存器:fp和sp寄存器。
  • 堆:在进程运行过程中,用于存储局部变量之外的变量。工作中常用的malloc函数、new操作符等可以从堆中申请内存。上面的栈很像数据结构中的栈,但这里的堆并不像数据结构的堆,其分配的方式是链表式,用brk()函数从操作系统批发内存,再零售给用户。
  • 数据段:通常指的段和data段,bss段内是未被初始化的静态变量,data段是在代码中已经初始化的静态变量。data段变大会导致启动速度变慢,bss段变大几乎不影响。因为bss段只需要预留位置,并没有真正的copy操作。相比data段增加的是具体的数据,bss段增加的只是数据描述信息。
  • 代码段:程序运行的机器指令,由代码编译产生。

64位的实际分布

对于一个iOS开发来说,目前大部分手机都是64位机器,还是需要对实际运行结果进行一些测试。
以下真机测试的机型是iPhone XS Max + iOS 14.5。

64位机器,进程内存地址从高到低分别是:
0xFFFF FFFF FFFF FFFF ⬇️
内核空间
用户空间-保留区域
扩展使用区域
系统共享库
栈空间
内存映射区域(mmap)
堆空间
BSS段
DATA段
Text段
0x0000 0000 0000 0000

iOS的内存分布探究

常见概念-堆、栈、数据段、代码段

堆和栈

用一段简单的代码,分别从堆和栈上面创建一块内存:

  char stack_address;   UIView *heap_view_address = [[UIView alloc] init];   NSLog(@"0x%016lx => stack 0x%016lx => heap", (long)&stack_address, (long)heap_view_address);

输出 0x16f4c5af7 => stack 0x100e0d8a0 => heap,可以大概知道栈和堆所在区域,0x16F4…是栈地址的开始,0x100E…是堆地址的开始。

数据段

bss段内是未被初始化的静态变量,data段是在代码中已经初始化的静态变量。

// 函数外-静态变量 static int vcStaticInt = 1024; static int vcStaticNotInit;  // 函数内 NSLog(@"0x%lx => data 0x%lx => bss", (long)&vcStaticInt, (long)&vcStaticNotInit);

vcStaticNotInit代表bss段,最终的地址是0x100945788
vcStaticInt代表data段,最终的地址是0x1009455f8

代码段

代码段是代码编译后的机器指令,可以用一个类来定位:

NSLog(@"class_address: 0x%lxn", (long)[ViewController class]);

最终输出的class_address是0x100945500。

将这几个地址的大小进行排序,可以看到有:
0x16F4C 5AF7(栈地址)
0x100E0 D8A0(堆地址)
0x10094 5788(bss段)
0x10094 55F8(data段)
0x10094 5500(Text段)

系统共享库

下面是两个不同App(bundle id不一样)在同手机上的运行crash日志,对比可以发现:在dyld之前的系统库地址不一样,在dyld之后的地址都是一样的。

iOS的内存分布探究

App中存在很多系统动态库,在启动时依赖dyld加载系统动态库到内存中。App依赖的具体系统动态库可能不同,但是都是iOS系统提供的。自然可以采用一种优化App启动速度方法:将所有的的系统依赖库按照固定的地址写在某个固定区域,这样只需保证App运行时这块内存不被使用,就能保证所有App启动时候不需要去装载所有的动态库。

内存映射区域

在栈空间的下方和堆空间的上方,有一块区域是内存映射区域。系统可以将文件的内容直接映射到内存,App可以通过mmap()方法请求将磁盘上文件的地址信息与进程用的虚拟逻辑地址进行映射。相比普通的读写文件,当App读取一个文件时有两步:先将文件从磁盘读取到物理内存,再从内核空间拷贝到用户空间。内存映射则可以减少操作系统的地址转换带来的消耗。

可以写一段mmap的代码来观察生成的地址

- (void)testMmap {   NSString *imagePathStr = [[NSBundle mainBundle] pathForResource:@"abc" ofType:@"png"];   size_t dataLength;   void *dataPtr;   // MapFile是自己写的mmap方法   int errorCode = MapFile([imagePathStr cStringUsingEncoding:NSUTF8StringEncoding], &dataPtr, &dataLength);   NSLog(@"mmapData:0x%lx, bytes_address:0x%lx, size:%d, error:%d", (long)dataPtr, (long)dataPtr, (long)dataLength, errorCode); }

最终输出的dataLength地址是0x1026b8000,size是18432,注意到这个地址是在上面的堆和栈之间。

用户空间-保留区域

这一块没有查到相关信息,如有资料求分享。以下是实际运行的分析。

“`
@interface TestOCObject : NSObject
@property (nonatomic, readonly, assign) char *name_buffer;
@end
@implementation TestOCObject {
char name[102400];
}

  • (char *)name_buffer {
    return name;
    }
    @end

  • (void)testHeapSize:(int)count {
    NSMutableArray *arr = [NSMutableArray new];

前言

最近遇到一些内存相关crash,排查问题过程中产生对进程内整个地址空间分布的疑惑。搜查了一番资料,网上关于Linux进程地址空间分布的介绍比较详细,但是iOS实际运行效果的比较少。
本文基于网上相关文章,进行实际测试,探究App实际运行过程中的地址分布。

正文

32位的分布情况

32位的机器,每个进程会有4G虚拟地址空间,较高的1G是从0xC0000000到0xFFFFFFFF的内核空间(Kernel Space ),较低的3G是从0x00000000到0xBFFFFFFF用户空间(User Space )。 内核空间中存放的是内核代码和数据,用户空间中存放的是App进程的代码和数据。这里地址指的都是虚拟地址空间,由操作系统负责映射为物理地址。
把最常用的几个概念堆、栈、数据段、代码段做一个地址从大到小的排序:
iOS的内存分布探究

  • 栈:在函数调用过程中,每个函数都会有一个相关的区域来存储函数的参数和局部变量,每次进行函数调用的时候系统都会往栈压入一个新的栈帧,在函数返回时清除。入栈和出栈的操作非常快,这个过程会用到两个寄存器:fp和sp寄存器。
  • 堆:在进程运行过程中,用于存储局部变量之外的变量。工作中常用的malloc函数、new操作符等可以从堆中申请内存。上面的栈很像数据结构中的栈,但这里的堆并不像数据结构的堆,其分配的方式是链表式,用brk()函数从操作系统批发内存,再零售给用户。
  • 数据段:通常指的段和data段,bss段内是未被初始化的静态变量,data段是在代码中已经初始化的静态变量。data段变大会导致启动速度变慢,bss段变大几乎不影响。因为bss段只需要预留位置,并没有真正的copy操作。相比data段增加的是具体的数据,bss段增加的只是数据描述信息。
  • 代码段:程序运行的机器指令,由代码编译产生。

64位的实际分布

对于一个iOS开发来说,目前大部分手机都是64位机器,还是需要对实际运行结果进行一些测试。
以下真机测试的机型是iPhone XS Max + iOS 14.5。

64位机器,进程内存地址从高到低分别是:
0xFFFF FFFF FFFF FFFF ⬇️
内核空间
用户空间-保留区域
扩展使用区域
系统共享库
栈空间
内存映射区域(mmap)
堆空间
BSS段
DATA段
Text段
0x0000 0000 0000 0000

iOS的内存分布探究

常见概念-堆、栈、数据段、代码段

堆和栈

用一段简单的代码,分别从堆和栈上面创建一块内存:

  char stack_address;   UIView *heap_view_address = [[UIView alloc] init];   NSLog(@"0x%016lx => stack 0x%016lx => heap", (long)&stack_address, (long)heap_view_address);

输出 0x16f4c5af7 => stack 0x100e0d8a0 => heap,可以大概知道栈和堆所在区域,0x16F4…是栈地址的开始,0x100E…是堆地址的开始。

数据段

bss段内是未被初始化的静态变量,data段是在代码中已经初始化的静态变量。

// 函数外-静态变量 static int vcStaticInt = 1024; static int vcStaticNotInit;  // 函数内 NSLog(@"0x%lx => data 0x%lx => bss", (long)&vcStaticInt, (long)&vcStaticNotInit);

vcStaticNotInit代表bss段,最终的地址是0x100945788
vcStaticInt代表data段,最终的地址是0x1009455f8

代码段

代码段是代码编译后的机器指令,可以用一个类来定位:

NSLog(@"class_address: 0x%lxn", (long)[ViewController class]);

最终输出的class_address是0x100945500。

将这几个地址的大小进行排序,可以看到有:
0x16F4C 5AF7(栈地址)
0x100E0 D8A0(堆地址)
0x10094 5788(bss段)
0x10094 55F8(data段)
0x10094 5500(Text段)

系统共享库

下面是两个不同App(bundle id不一样)在同手机上的运行crash日志,对比可以发现:在dyld之前的系统库地址不一样,在dyld之后的地址都是一样的。

iOS的内存分布探究

App中存在很多系统动态库,在启动时依赖dyld加载系统动态库到内存中。App依赖的具体系统动态库可能不同,但是都是iOS系统提供的。自然可以采用一种优化App启动速度方法:将所有的的系统依赖库按照固定的地址写在某个固定区域,这样只需保证App运行时这块内存不被使用,就能保证所有App启动时候不需要去装载所有的动态库。

内存映射区域

在栈空间的下方和堆空间的上方,有一块区域是内存映射区域。系统可以将文件的内容直接映射到内存,App可以通过mmap()方法请求将磁盘上文件的地址信息与进程用的虚拟逻辑地址进行映射。相比普通的读写文件,当App读取一个文件时有两步:先将文件从磁盘读取到物理内存,再从内核空间拷贝到用户空间。内存映射则可以减少操作系统的地址转换带来的消耗。

可以写一段mmap的代码来观察生成的地址

- (void)testMmap {   NSString *imagePathStr = [[NSBundle mainBundle] pathForResource:@"abc" ofType:@"png"];   size_t dataLength;   void *dataPtr;   // MapFile是自己写的mmap方法   int errorCode = MapFile([imagePathStr cStringUsingEncoding:NSUTF8StringEncoding], &dataPtr, &dataLength);   NSLog(@"mmapData:0x%lx, bytes_address:0x%lx, size:%d, error:%d", (long)dataPtr, (long)dataPtr, (long)dataLength, errorCode); }

最终输出的dataLength地址是0x1026b8000,size是18432,注意到这个地址是在上面的堆和栈之间。

用户空间-保留区域

这一块没有查到相关信息,如有资料求分享。以下是实际运行的分析。

“`
@interface TestOCObject : NSObject
@property (nonatomic, readonly, assign) char *name_buffer;
@end
@implementation TestOCObject {
char name[102400];
}

  • (char *)name_buffer {
    return name;
    }
    @end

  • (void)testHeapSize:(int)count {
    NSMutableArray *arr = [NSMutableArray new];

前言

最近遇到一些内存相关crash,排查问题过程中产生对进程内整个地址空间分布的疑惑。搜查了一番资料,网上关于Linux进程地址空间分布的介绍比较详细,但是iOS实际运行效果的比较少。
本文基于网上相关文章,进行实际测试,探究App实际运行过程中的地址分布。

正文

32位的分布情况

32位的机器,每个进程会有4G虚拟地址空间,较高的1G是从0xC0000000到0xFFFFFFFF的内核空间(Kernel Space ),较低的3G是从0x00000000到0xBFFFFFFF用户空间(User Space )。 内核空间中存放的是内核代码和数据,用户空间中存放的是App进程的代码和数据。这里地址指的都是虚拟地址空间,由操作系统负责映射为物理地址。
把最常用的几个概念堆、栈、数据段、代码段做一个地址从大到小的排序:
iOS的内存分布探究

  • 栈:在函数调用过程中,每个函数都会有一个相关的区域来存储函数的参数和局部变量,每次进行函数调用的时候系统都会往栈压入一个新的栈帧,在函数返回时清除。入栈和出栈的操作非常快,这个过程会用到两个寄存器:fp和sp寄存器。
  • 堆:在进程运行过程中,用于存储局部变量之外的变量。工作中常用的malloc函数、new操作符等可以从堆中申请内存。上面的栈很像数据结构中的栈,但这里的堆并不像数据结构的堆,其分配的方式是链表式,用brk()函数从操作系统批发内存,再零售给用户。
  • 数据段:通常指的段和data段,bss段内是未被初始化的静态变量,data段是在代码中已经初始化的静态变量。data段变大会导致启动速度变慢,bss段变大几乎不影响。因为bss段只需要预留位置,并没有真正的copy操作。相比data段增加的是具体的数据,bss段增加的只是数据描述信息。
  • 代码段:程序运行的机器指令,由代码编译产生。

64位的实际分布

对于一个iOS开发来说,目前大部分手机都是64位机器,还是需要对实际运行结果进行一些测试。
以下真机测试的机型是iPhone XS Max + iOS 14.5。

64位机器,进程内存地址从高到低分别是:
0xFFFF FFFF FFFF FFFF ⬇️
内核空间
用户空间-保留区域
扩展使用区域
系统共享库
栈空间
内存映射区域(mmap)
堆空间
BSS段
DATA段
Text段
0x0000 0000 0000 0000

iOS的内存分布探究

常见概念-堆、栈、数据段、代码段

堆和栈

用一段简单的代码,分别从堆和栈上面创建一块内存:

  char stack_address;   UIView *heap_view_address = [[UIView alloc] init];   NSLog(@"0x%016lx => stack 0x%016lx => heap", (long)&stack_address, (long)heap_view_address);

输出 0x16f4c5af7 => stack 0x100e0d8a0 => heap,可以大概知道栈和堆所在区域,0x16F4…是栈地址的开始,0x100E…是堆地址的开始。

数据段

bss段内是未被初始化的静态变量,data段是在代码中已经初始化的静态变量。

// 函数外-静态变量 static int vcStaticInt = 1024; static int vcStaticNotInit;  // 函数内 NSLog(@"0x%lx => data 0x%lx => bss", (long)&vcStaticInt, (long)&vcStaticNotInit);

vcStaticNotInit代表bss段,最终的地址是0x100945788
vcStaticInt代表data段,最终的地址是0x1009455f8

代码段

代码段是代码编译后的机器指令,可以用一个类来定位:

NSLog(@"class_address: 0x%lxn", (long)[ViewController class]);

最终输出的class_address是0x100945500。

将这几个地址的大小进行排序,可以看到有:
0x16F4C 5AF7(栈地址)
0x100E0 D8A0(堆地址)
0x10094 5788(bss段)
0x10094 55F8(data段)
0x10094 5500(Text段)

系统共享库

下面是两个不同App(bundle id不一样)在同手机上的运行crash日志,对比可以发现:在dyld之前的系统库地址不一样,在dyld之后的地址都是一样的。

iOS的内存分布探究

App中存在很多系统动态库,在启动时依赖dyld加载系统动态库到内存中。App依赖的具体系统动态库可能不同,但是都是iOS系统提供的。自然可以采用一种优化App启动速度方法:将所有的的系统依赖库按照固定的地址写在某个固定区域,这样只需保证App运行时这块内存不被使用,就能保证所有App启动时候不需要去装载所有的动态库。

内存映射区域

在栈空间的下方和堆空间的上方,有一块区域是内存映射区域。系统可以将文件的内容直接映射到内存,App可以通过mmap()方法请求将磁盘上文件的地址信息与进程用的虚拟逻辑地址进行映射。相比普通的读写文件,当App读取一个文件时有两步:先将文件从磁盘读取到物理内存,再从内核空间拷贝到用户空间。内存映射则可以减少操作系统的地址转换带来的消耗。

可以写一段mmap的代码来观察生成的地址

- (void)testMmap {   NSString *imagePathStr = [[NSBundle mainBundle] pathForResource:@"abc" ofType:@"png"];   size_t dataLength;   void *dataPtr;   // MapFile是自己写的mmap方法   int errorCode = MapFile([imagePathStr cStringUsingEncoding:NSUTF8StringEncoding], &dataPtr, &dataLength);   NSLog(@"mmapData:0x%lx, bytes_address:0x%lx, size:%d, error:%d", (long)dataPtr, (long)dataPtr, (long)dataLength, errorCode); }

最终输出的dataLength地址是0x1026b8000,size是18432,注意到这个地址是在上面的堆和栈之间。

用户空间-保留区域

这一块没有查到相关信息,如有资料求分享。以下是实际运行的分析。

“`
@interface TestOCObject : NSObject
@property (nonatomic, readonly, assign) char *name_buffer;
@end
@implementation TestOCObject {
char name[102400];
}

  • (char *)name_buffer {
    return name;
    }
    @end

  • (void)testHeapSize:(int)count {
    NSMutableArray *arr = [NSMutableArray new];

部分转自互联网,侵权删除联系

赞(0) 打赏
部分文章转自网络,侵权联系删除b2bchain区块链学习技术社区 » iOS的内存分布探究求职学习资料
分享到: 更多 (0)

评论 抢沙发

  • 昵称 (必填)
  • 邮箱 (必填)
  • 网址

b2b链

联系我们联系我们