美文网首页
第二十四节—内存(一)基本知识

第二十四节—内存(一)基本知识

作者: L_Ares | 来源:发表于2020-11-13 17:24 被阅读0次

本文为L_Ares个人写作,以任何形式转载请表明原文出处。

首先,要知道iOS的内存管理,还是要具备常规操作系统的基本内存知识的。这些基础的概念型知识,无论是PC移动端还是什么blabla的,都是一个最基层的东西,概念如果搞不清楚,很难去理解iOS的内存管理。

一、内存的基础知识

1. 什么是内存

内存是用于存放数据的硬件。

2. 内存能做什么

程序(也就是你的代码)是要放到内存中才可以被CPU处理的。

3. 内存的基本存储单元

在内存中,信息都是以二进制形式进行存储、运算、处理和传输的。信息的存储单位有位(bit)字节(byte)字(word)等等几种单位。

  • 位(bit) : 计算机中数据的最小单位。二进制数中的一个数位,可以是0或者1。

  • 字节(byte) : 计算机中数据的基本单位每8位(bit)组成一个字节(byte),也就是常说的 : 1byte = 8bit。各种信息或者说数据在计算机中进行存储或者处理,至少需要1字节

  • 字(word) : 两个字节称为一个字。常见的汉字就是一字。1word = 2byte = 16bit

图1.3.0.png

4. 地址

地址就是内存的索引编号,通过这个索引编号,就可以存取对应内存的数据。

地址一般分为 : 物理地址逻辑地址

物理地址(绝对地址) : 数据实际在内存中存放的地址。

逻辑地址(相对地址) : 实际生成指令的时候,数据不一定会被分配到哪里去,所以编译生成的指令中常用的就是逻辑地址。

二、内存五大区

先上图,看一下五大区是什么样子,是做什么的。

图2.0.0.png

1. 栈区

栈区是内存模型的叫法,栈是数据结构,因为本节是说内存管理的一些基本知识,就只说栈区,不说栈了,栈会在数据结构相应的章节中再说。

1.1 栈区特性
  • 栈区是内存模型,是一块连续的内存区域,在运行时分配
  • 内存中,栈区处于相对较高的地址,并且由高地址低地址扩展。
  • 栈区的栈顶地址栈区容量都是由系统定好的。具体大小是在进程分配时确定的,栈区内存是由编译器自动分配和释放的,所以栈区是不由我们控制的。
  • 栈区主要用来存放的是函数的参数值局部变量OC中对象的指针等。
  • iOS中,栈区的地址空间一般以0x7开头。
1.2 栈区优缺点
  • 优点 :
    • 栈区内存由于是内置于系统CPU的指令集自动分配和释放,也就是说用寄存器来操作,所以效率高速度快
  • 缺点 :
    • 栈区可分配的内存大小是有限的,并且因为不由我们操控,所以数据不灵活
      比如 : iOS主线程的大小只有1M。其他的二级线程大小只有512K,即使是Mac,也只有8M

2. 堆区

堆区也是内存模型的叫法,堆是数据结构,所以还是只说堆区,堆放在数据结构中说。

2.1 堆区特性
  • 堆区是内存模型,是不连续的内存区域,在运行时分配
  • 堆区相对处于较低的地址,堆区是由低地址高地址扩展的。
  • 堆区的大小是动态的。相比栈区要大一些。堆区是手动进行分配和释放的,如果分配后没有手动释放,可能会由系统回收,如果系统没有回收,则会造成内存泄漏。
  • 堆区在OC中主要用来存放allocnew开辟空间时创建的对象,还有blockcopy的数据。在C中则是malloc、calloc、realloc分配的内存空间。
  • iOS中,堆区的地址空间一般以0x6开头。
2.2 堆区优缺点
  • 优点 :
    • 使用起来方便,相对大一点,数据应用面也广一点。
  • 缺点 :
    • 速度慢,因为找一个空闲的堆区要遍历整个系统中记录空闲内存地址的链表。找到第一个空间大于所申请空间的堆结点, 然后把它从空闲内存地址的链表中删除,然后才将该结点的内存空间分配给程序。
    • 手动管理,手动管理就容易产生内存碎片。
2.3 堆区栈区的联合使用

这个其实就是在访问某一个对象的时候,一般先拿对象在栈区的指针地址,然后通过对象的指针地址再去堆区找对象的实际地址

3. 静态区(全局区)

静态区或者说全局区,它们是一个东西,因为静态变量和全局变量都存储在这里。静态区(全局区)是编译时分配内存空间

说几点特性 :

  • 在程序的运行过程中,静态区(全局区)的数据是一直都存在的,要等到程序结束后,由系统来释放。
  • 未初始化的静态变量和全局变量在一块区域,被叫做BSS区(.bss).
  • 初始化的静态变量和全局变量在未初始化的相邻区域,被叫做数据区(.data)
  • 静态变量作用域和全局变量不同而已,生命周期相同,全局变量作用域是整个项目文件,除了不包括全局变量定义的文件。静态变量的作用域则只是定义了它的文件,并且只初始化一次就一直会到生命周期结束。

4. 常量区

常量区也是编译时分配内存空间,也是程序结束后,由系统释放

  • 顾名思义,常量区存放的都是常量,一般也被叫做.rodata区,这也就限制了它的能力,常量吗,是不让你改的。
  • 比如说你常用的一些字符串常量,它们不需要被更改,可能会被你多次使用,所以在程序运行之前,就可以给他们分配内存空间了。
  • iOS中常量区一般以0x1开头。

5. 代码区

代码区也是编译时分配内存空间,一般也被叫做.text区,存放的就是程序运行时的代码,代码会编译成二进制存放入代码区。

6. 例子

图2.6.0.png
  • number : 局部变量。地址一般以0x7开头,存放在栈区

  • str : 字符串常量对象

    • 对象本身 : str就是个字符串常量对象,地址一般以0x1开头,存放在常量区
    • 对象的指针 : str是个对象,有指针,地址一般以0x7开头,存放在栈区
  • obj : 普通对象

    • 对象本身 : 地址一般以0x6开头,存放在堆区
    • 对象的指针 : 地址一般以0x7开头,存放在栈区

三、举例

放一些例子吧,一些虽然比较老,但是很经典的例子,基本上看过这些例子,能玩的转的,就可以说对内存的五大区有了一个初步的掌握了。从上到下的举例,上面已经有了关于堆区、栈区的一个简单的例子了,就不说堆区、栈区了,从静态区(全局区)来举例。

静态区(全局区)

这里我发现很多人是拎不清静态是不是允许改变的,没有人说普通的静态变量或者全局变量不允许改变,看好它们的名字——变量!变量怎么可能不允许变呢。但是变的是哪个量,要看文件!看文件!看文件!文件才是它的作用域限制。

准备 :
(1). 创建一个Project---> App
(2). 创建一个继承于NSObjectJDPerson类,再创建一个JDPerson的分类JDPerson+JD
(3). ViewController中引入头文件,JDPerson.hJDPerson+JD.h

JDPerson.h

#import <Foundation/Foundation.h>

static int jd_person_num = 100;

NS_ASSUME_NONNULL_BEGIN

@interface JDPerson : NSObject

- (void)study;

+ (void)eat;

@end

NS_ASSUME_NONNULL_END

JDPerson.m

#import "JDPerson.h"

@implementation JDPerson

- (void)study
{
    jd_person_num++;
    NSLog(@"\n JDPerson内部 *** *** 地址 : %p --- 内容 : %d \n",&jd_person_num,jd_person_num);
}

+ (void)eat
{
    jd_person_num++;
    NSLog(@"\n JDPerson内部 *** *** 地址 : %p --- 内容 : %d \n",&jd_person_num,jd_person_num);
}

@end

JDPerson+JD.h

#import "JDPerson.h"

NS_ASSUME_NONNULL_BEGIN

@interface JDPerson (JD)

- (void)jd_category_instance_method;

@end

NS_ASSUME_NONNULL_END

JDPerson+JD.m

#import "JDPerson+JD.h"

@implementation JDPerson (JD)

- (void)jd_category_instance_method
{

    NSLog(@"\n JDPerson+JD 内部 *** *** 地址 : %p --- 内容 : %d \n",&jd_person_num,jd_person_num);
}

@end

ViewController.m

- (void)jd_mem_static
{
    JDPerson *person = [[JDPerson alloc] init];
    
    //看JDPerson中定义的静态变量
    NSLog(@"\n viewController 中 *** *** 地址 : %p --- 内容 : %d \n",&jd_person_num,jd_person_num);
    
    //改变JDPerson中定义的静态变量
    jd_person_num = 888;
    NSLog(@"\n viewController 中 *** *** 地址 : %p --- 内容 : %d \n",&jd_person_num,jd_person_num);
    
    //JDPerson自己内部查看静态变量
    [person study];
    
    //对比看一下ViewController中的静态变量
    NSLog(@"\n viewController 中 *** *** 地址 : %p --- 内容 : %d \n",&jd_person_num,jd_person_num);
    
    //JDPerson调用类方法,再查看内部的静态变量
    [JDPerson eat];
    
    //对比看一下ViewController中的静态变量
    NSLog(@"\n viewController 中 *** *** 地址 : %p --- 内容 : %d \n",&jd_person_num,jd_person_num);
    
    //JDPerson分类中拿到的静态变量
    [person jd_category_instance_method];
    
}

结果 :

图3.1.0.png

结论 :

静态变量在不同的文件中是不一样的,自己玩自己的。静态全局变量,作用域是定义它的文件的内部,自己文件内部都一样,生命周期是程序结束。

这篇只是内存管理的序篇,因为内存管理中还要接触到包括线程、锁、block等等在内的东西,所以其他的章节会放到这些东西都说明白了以后再说,这样更容易理解。

相关文章

  • 第二十四节—内存(一)基本知识

    本文为L_Ares个人写作,以任何形式转载请表明原文出处。 首先,要知道iOS的内存管理,还是要具备常规操作系统的...

  • Android的垃圾回收与内存泄露

    标签(空格分隔): Android 内存泄漏的基本知识请见博客一如何高效使用handler避免内存泄漏请见博客二 ...

  • iOS逆向:ARM64汇编基础

    目录一,基本知识二,搭建环境三,通用寄存器四,基础指令五,跳转指令六,内存指令七,堆栈八,实战练习 一,基本知识 ...

  • Java的深拷贝与浅拷贝

    基本知识 Java在运行时的内存使用分两块:栈内存与堆内存。 只就变量而言,栈内存上分配一些基本类型的变量(如in...

  • block循环引用问题

    在讲block的循环引用问题之前,我们需要先了解一下iOS的内存管理机制和block的基本知识。 iOS的内存管理...

  • iOS 利用Insruments 工具集 检测内存泄露(一)

    首先复习下内存管理基本知识 MRC: iOS5.0之前,OC内存管理遵循谁创建,谁释放,谁引用,谁管理的机制,当创...

  • Block中的循环引用

    在讲block的循环引用问题之前,我们需要先了解一下iOS的内存管理机制和block的基本知识 iOS的内存管理机...

  • 苹果工程师教你电脑硬件基本知识

    上回小伟哥写了一篇关于 iPhone 内存的基本知识,这次小伟哥打算以内存为切入点,再写一写我们常用的电脑.看完了...

  • 说说内存泄漏?

    一般什么情况下会导致内存泄漏问题? 基本知识储备 内存泄漏程序不再使用对象 A,但是因为 A 持有另外一个生命周期...

  • JVM基本知识-内存结构

    JVM的内存结构 jdk8 jvm说明根据oracle官方文档的描述,可以总结JVM运行时内存的划分 Java虚拟...

网友评论

      本文标题:第二十四节—内存(一)基本知识

      本文链接:https://www.haomeiwen.com/subject/xujmbktx.html