iOS开发中的内存分配与分区

RAM 和 ROM 就是具体的存储空间,统称为存储器

RAM(Random access memory):运行内存, CPU 可以直接访问,读写速度非常快,但是不能掉电存储分为 动态存储(DRAM): 存储速度有点慢,需要定期刷新,就是我们平常说的内存条,手机中的内存也是指它

静态存储(SRAM): 速度快,我们平常说的一级缓存,二级缓存

ROM(Read only memory): 存储型内存可以掉电存储,例如 SD 卡/Flash

RAM 不能掉电存储,所以我们的 APP 程序,刷机包,下载的文件等都存储在 ROM 中,手机中使用的 ROM 基本上都是 NandLand,CPU 不能直接访问,而是需要文件系统/驱动程序(嵌入式中的EMC)将其读取到 RAM 中,RAM 的速度比较快

内存(RAM)分区:

栈区(stack): 栈是向低地址扩展的数据结构,它是一块连续的内存区域,栈的空间很小,大约是1~2M,由编译器操作,会存储一些局部变量,函数跳转时现场保护,这些都是系统帮我们实现,所以大量的局部变量,深递归,函数循环的调用都有可能耗尽栈内存而造成程序崩溃

堆区(heap): 堆区是想高地址扩展的数据结构,不连续的内存区域,系统用链表存储空闲地址,链表遍历由低到高向一般由程序员管理,比如 alloc 申请内存, free 释放内存,我们创建的对象也都存储在这

全局区(静态区 static): 全局白能量和静态变量的存储是放在一块的,初始化的全局变量和静态变量在一块区域,为初始化的全局变量和局部变量存储在一块相邻的区域,程序结束后释放.(嵌入式系统中全局区分为未初始化的 BSS 段 和初始化的 data 段)

常量区: 存储常量字符串和 const 常量

代码区: 存放代码,

虚拟内存简介

什么是VM Regions呢?要知道这个首先要了解什么是虚拟内存。当我们向系统申请内存时,系统并不会给你返回物理内存的地址,而是给你一个虚拟内存地址。每个进程都拥有相同大小的虚拟地址空间,对于32位的进程,可以拥有4GB的虚拟内存,64位进程则更多,可达18EB。只有我们开始使用申请到的虚拟内存时,系统才会将虚拟地址映射到物理地址上,从而让程序使用真实的物理内存

经典面试题:
  1. 这段代码有什么问题,怎么优化
- (void) interviewForStackMemory
{
   for (int i = 0;i<1000000;i++)
   {
      NSString *str = @"小萝莉";
      str = [str stringByAppendingString:@"是春哥的"];
   }
}
  1. 以下数据和变量都存储在那一类内存中
int a;int c = 10;
NSString *name1;NSString *name2 = @"lxx";
- (void) interviewForStore
{
   int b;
   NSString *name3 = @"hyc";
   NSString *name4;
   char h[] = "plm";
   static int k = 0;

   NSString *m = [[NSString alloc]initWithFormat:@"le"];
}
  1. sizeof计算对象所占内存大小
int main()  
{  
    //字符数组  
    char arr[] = "abcdef";//7               (1)  
    printf("%d\n", sizeof(arr));//7         (2)  
    printf("%d\n", sizeof(arr+0));//4       (3)  
    printf("%d\n", sizeof(*arr));//1        (4)  
    printf("%d\n", sizeof(arr[1]));//1      (5)  
    printf("%d\n", sizeof(&arr));//4.       (6)  
    printf("%d\n", sizeof(&arr+1));//4      (7)  
    printf("%d\n", sizeof(&arr[0]+1));//4   (8)  

    return 0;  
}  
(1)该字符数组中有7个元素,其中最后一个元素为“\0”

(2)括号中直接加arr表示指整个数组的所占内存,为7个字节
(3)arr+常量;数组名arr表示为数组首元素的地址,所以此时还是数组首元素,地址的大小为4
(4)arr为地址,对其*,则为里面的元素,元素为字符型,所以占1个字节 
(5)相当于第二个元素,为字符型占1个字节. 
(6)数组的地址,地址在系统占4个字节.
(7)跳过整个数组,依然为地址占4个字节
(8)第二个元素的地址,地址所占内存大小依然为4个字节
  1. struct占内存大小
在GNU GCC编译器中,遵循的准则有些区别,对齐模数不是像上面所述的那样,根据最宽的基本数据类型来定。在GCC中,对齐模数的准则是:对齐模数最大只能是4,也就是说,即使结构体中有double类型,对齐模数还是4,所以对齐模数只能是1,2,4。而且在上述的三条中,第2条里,offset必须是成员大小的整数倍,如果这个成员大小小于等于4则按照上述准则进行,但是如果大于4了,则结构体每个成员相对于结构体首地址的偏移量(offset)只能按照是4的整数倍来进行判断是否添加填充。
struct A
{
           int a;
          double b;
           char c;
};
struct B
{
           double b;
            char c;
            int a;
};
struct T
{
  char ch;
  double   d   ;
};

1、结构体A首先给int a 分配四个字节,并且以4个字节对齐;然后给double b分配8个字节,发现以4个字节对齐不行,就以8个字节对齐,前面只有int a ,所以int a将占用8个字节;最后为了对齐,将给char c 也分配8给字节,所以结构体A占用了24个字节。
2、结构体B首先给double 分配8个字节,并且以8给字节对齐;然后给char c分配8给字节;最后给int a分配空间的时候发现,前面空有7个字节空间可以放下int a,int a 就和char c一起占用8个字节,所以结构体B占用了16个字节
3、在GCC下,sizeof(T)应该等于12个字节


struct A
{

 int a;     0-3

               4-7     要填充(padding)以保证内存对齐的原则
 double b; 8-15
 char c[9]; 16-24
};

首先给a分配内存,因为int占四个字节<pack大小(8个字节),所以按照4个字节对齐,起始位值为0,0%4=0,a最后占的内存为0-3;

接着给b分配内存,double占8个字节,所以按照8个字节对齐,起始位为8(8%8=0),b为8-15;

最后char占一个字节,所以c为16-24;

因此结构体A的大小为8+8+9 = 25;有因为25不是结构体中最大占用内存类型double类型大小(8)的整数倍,所以A最后的大小是32;

results matching ""

    No results matching ""