【iOS】——内存对齐

news2024/9/21 2:43:50

内存对齐是什么

内存对齐指的是数据在内存中的布局方式,它确保每个数据类型的起始地址能够满足该类型对齐的要求。这是因为现代处理器在访问内存时,如果数据的起始地址能够对齐到一定的边界,那么访问速度会更快。这种对齐通常是基于数据类型大小的倍数。内存对齐包括两种相互独立又相互关联的部分:基本数据对齐和结构体数据对齐。

内存对齐的原因

内存对齐的主要原因有两个:性能和硬件限制。

性能原因

  1. 硬件访问效率:现代处理器设计为能够更高效地访问对齐的数据。这是因为处理器内部的总线宽度和寄存器大小通常决定了数据的最佳访问粒度。例如,如果一个处理器的寄存器大小是64位,那么访问64位对齐的长整型数据会比访问未对齐的数据更快,因为后者可能需要多次内存访问才能装载完整数据。
  2. 缓存性能:现代处理器使用多层次的缓存(L1, L2, L3等)来提高数据访问速度。数据对齐有助于缓存行的高效使用,减少缓存未命中,从而提高整体性能。
  3. 并发访问:在多核或多处理器系统中,对齐数据可以减少访问冲突,因为每个处理器或核心可以更有效地访问自己负责的内存区域。

硬件限制

  1. 硬件异常:某些硬件平台可能无法访问未对齐的内存地址,访问未对齐数据可能导致硬件异常或陷阱,这会极大地降低程序的性能,甚至导致程序崩溃。
  2. 字节序问题:虽然内存对齐与字节序(endianness)直接关联不大,但在处理字节序敏感的数据时,对齐可以避免额外的字节交换操作,从而提高性能。

内存对齐原则

每个特定平台上的编译器都有自己的默认“对齐系数”(也叫对齐模数)。程序员可以通过预编译命令#pragma pack(n),n=1,2,4,8,16来改变这一系数,其中的n就是你要指定的“对齐系数”。在ios中,Xcode默认为#pragma pack(8),即8字节对齐

  1. 数据成员对齐原则

    • 结构体的第一个数据成员放置在offset为0的位置。

    • 后续的数据成员将被放置在它们各自自然对齐的地址上,即如果成员是4字节的整型,它将被放置在4字节对齐的位置;如果是8字节的双精度浮点型,它将被放置在8字节对齐的位置。

  2. 结构体总大小对齐原则

    • 结构体的总大小必须是其内部最大成员大小的整数倍。如果结构体的自然大小不符合这个条件,编译器会在最后一个成员之后填充一些额外的字节,直到整个结构体的大小满足对齐要求。
  3. 自然边界对齐原则:: 每种数据类型都有一个“自然”边界,这是指数据类型大小的整数倍地址。例如,一个int类型在32位系统中通常占用4个字节,因此它应该在地址能够被4整除的位置开始。同样,一个short类型(通常2个字节)应该在能够被2整除的地址开始,以此类推。

在这里插入图片描述

可以将内存对齐原则可以理解为以下几点:

  • 【原则一】 数据成员的对齐规则可以理解为min(m, n) 的公式, 其中 m表示当前成员的开始位置, n表示当前成员所需要的位数。如果满足条件 m 整除 n (即 m % n == 0), nm 位置开始存储, 反之继续检查 m+1 能否整除 n, 直到可以整除, 从而就确定了当前成员的开始位置。
  • 【原则二】数据成员为结构体:当结构体嵌套了结构体时,作为数据成员的结构体的自身长度作为外部结构体的最大成员的内存大小,比如结构体a嵌套结构体b,b中有char、int、double等,则b的自身长度为8
  • 【原则三】最后结构体的内存大小必须是结构体中最大成员内存大小的整数倍,不足的需要补齐。

下面举个例子:

struct Example {
    char c;
    int i;
    double d;
};

char的大小为1字节,int的大小为4字节,而double的大小为8字节。由于double是最大的成员,所以整个结构体的大小必须是8字节的倍数。char会放在offset为0的位置,int会放在offset为4的位置(因为它需要4字节对齐),而double会放在offset为8的位置(因为它需要8字节对齐)。由于double之后没有更多的成员,所以结构体的大小就刚好是16字节,正好是8字节的倍数。

结构体嵌套的对齐

当结构体嵌套其他结构体时,不仅要考虑单个成员的对齐,还要考虑嵌套结构体本身的对齐和整个复合结构体的对齐。

遵循以下原则:

  • 嵌套的结构体成员将被视为一个单一的整体,其对齐需求基于嵌套结构体中最大成员的对齐需求。
  • 如果嵌套的结构体中有自己的对齐需求(比如含有double类型),那么在外部结构体中,嵌套结构体将按照其内部最大成员的对齐需求进行对齐。
  • 外部结构体的对齐取决于其所有成员(包括嵌套结构体)的最大对齐需求。
  • 如果嵌套结构体的对齐需求大于外部结构体中任何其他成员的对齐需求,那么整个外部结构体将按照嵌套结构体的对齐需求对齐。
  • 当嵌套结构体不符合其对齐需求时,编译器会在嵌套结构体之前插入填充字节。嵌套结构体结束后的下一个成员需要更大的对齐,编译器会在嵌套结构体之后插入填充字节。
  • 整个结构体的大小也必须满足结构体中最大成员的对齐需求。如果结构体的自然大小不满足这一要求,编译器会在结构体末尾添加额外的填充字节。

获取内存大小的方式

获取内存大小的三种方式分别是:

  • sizeof
  • class_getInstanceSize
  • malloc_size
sizeof

1、sizeof是一个操作符,不是函数

2、我们一般用sizeof计算内存大小时,传入的主要对象是数据类型,这个在编译器的编译阶段(即编译时)就会确定大小而不是在运行时确定。

3、sizeof最终得到的结果是该数据类型占用空间的大小

class_getInstanceSize

这个方法是runtime提供的api,用于获取类的实例对象所占用的内存大小,并返回具体的字节数,其本质就是获取实例对象中成员变量的内存大小。采用8字节对齐,参照的对象的属性内存大小

malloc_size

这个函数是获取系统实际分配的内存大小。采用16字节对齐,参照的整个对象的内存大小,对象实际分配的内存大小必须是16的整数倍

目前已知的16字节内存对齐算法有两种

  • alloc源码分析中的align16
  • malloc源码分析中的segregated_size_to_fit

下面举个例子:

#import <Foundation/Foundation.h>
#import <objc/runtime.h>
#import <malloc/malloc.h>

int main(int argc, const char * argv[]) {
    @autoreleasepool {
        NSObject *objc = [[NSObject alloc] init];
        NSLog(@"objc对象类型占用的内存大小:%lu",sizeof(objc));
        NSLog(@"objc对象实际占用的内存大小:%lu",class_getInstanceSize([objc class]));
        NSLog(@"objc对象实际分配的内存大小:%lu",malloc_size((__bridge const void*)(objc)));
    }
    return 0;
}

在这里插入图片描述

内存优化(属性重排)

结构体内存大小与结构体成员内存大小的顺序有关:

如果是结构体中数据成员是根据内存从小到大的顺序定义的,根据内存对齐规则来计算结构体内存大小,需要增加有较大的内存padding即内存占位符,才能满足内存对齐规则,比较浪费内存

如果是结构体中数据成员是根据内存从大到小的顺序定义的,根据内存对齐规则来计算结构体内存大小,我们只需要补齐少量内存padding即可满足堆存对齐规则,这种方式就是苹果中采用的,利用空间换时间,将类中的属性进行重排,来达到优化内存的目的

在字节对齐算法中,对齐的主要是对象,对象的本质是objc_object结构体。

对于一个对象来说,其真正的对齐方式8字节对齐,8字节对齐已经足够满足对象的需求了

apple系统为了防止一切的容错,采用的是16字节对齐的内存,主要是因为采用8字节对齐时,两个对象的内存会紧挨着,显得比较紧凑,而16字节比较宽松,利于苹果以后的扩展。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1935894.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

客户中心应急管理的作用和特征

近些年作为事故、灾难等风险的预防主体和第一响应者&#xff0c;客户中心的应急管理取得了较大进展&#xff0c;但总体上仍存在很多薄弱环节&#xff0c;如安全事故频发&#xff0c;自然灾害、公共卫生、社会安全事件等给运营机构带来了多方面的不利影响。从信息角度看&#xf…

20240720 每日AI必读资讯

OpenAI 推出GPT-4o mini取代 GPT 3.5&#xff01; - 性能超越 GPT 4&#xff0c;而且更快更便宜 - 该模型在MMLU上得分为82%&#xff0c;在LMSYS排行榜上的聊天偏好测试中表现优于GPT-4。 - GPT-4o mini的定价为每百万输入标记15美分和每百万输出标记60美分&#xff0c;比之…

【golang-ent】go-zero框架 整合 ent orm框架 | 解决left join未关联报错的问题

一、场景 1、子表&#xff1a;cp_member_point_history cp_member_point_history表中字段&#xff1a;cp_point_reward_id 是cp_point_reward的主键id 当本表中的cp_point_reward_id字段为0&#xff08;即&#xff1a;没有可关联主表的&#xff09; CREATE TABLE cp_member_poi…

项目开发之文件上传 (秒传、断点续传、分片上传)(看这一篇就懂了)

目录&#xff1a; 前言秒传什么是秒传核心逻辑代码实现 小文件上传什么是小文件上传核心逻辑代码实现 分片上传什么是分片上传核心逻辑代码实现 断点续传什么是断点续传核心代码实现 前言 文件上传在项目开发中再常见不过了&#xff0c;大多项目都会涉及到图片、音频、视频、文…

npm安装依赖包报错,npm ERR! code ENOTFOUND

一、报错现象&#xff1a; npm WARN registry Unexpected warning for https://registry.npmjs.org/: Miscellaneous Warning ETIMEDOUT: request to https://registry.npmjs.org/vue failed, reason: connect ETIMEDOUT 104.16.23.35:443 npm WARN registry Using stale data…

Python | Leetcode Python题解之第235题二叉搜索树的最近公共祖先

题目&#xff1a; 题解&#xff1a; class Solution:def lowestCommonAncestor(self, root: TreeNode, p: TreeNode, q: TreeNode) -> TreeNode:ancestor rootwhile True:if p.val < ancestor.val and q.val < ancestor.val:ancestor ancestor.leftelif p.val >…

【力扣】最小栈

&#x1f525;博客主页&#xff1a; 我要成为C领域大神&#x1f3a5;系列专栏&#xff1a;【C核心编程】 【计算机网络】 【Linux编程】 【操作系统】 ❤️感谢大家点赞&#x1f44d;收藏⭐评论✍️ 本博客致力于知识分享&#xff0c;与更多的人进行学习交流 设计一个支持 push…

object-C 解答算法:合并两个有序数组(leetCode-88)

合并两个有序数组(leetCode-88) 题目如下图:(也可以到leetCode上看完整题目,题号88) 首先搞懂,什么叫“非递减顺序” 非递减顺序,是指一个序列中的元素从前往后&#xff08;或从左到右&#xff09;保持不减少或相等。 这意味着序列中的元素可以保持相同的值&#xff0c;但不会…

c++ pc输入法例子

1、微软开源demo Windows-classic-samples/Samples/IME at master jiangdon2007/Windows-classic-samples (github.com) 2、打开SampleIME.sln 编译【32位或者64位】 3、将SampleIME.dll 和SampleIMESimplifiedQuanPin.txt 放在同一个目录 4、注册 regsvr32 SampleIME.dl…

spring 5.3.x 、6.1.x、6.0.x 源码本地编译运行

参考大佬文章&#xff0c;完美完成本地idea spring源码编译和demo测试 参考链接&#xff08;spring5.3.x&#xff09; spring5.3.x源码阅读环境搭建 下面是spring6.0.x参考 spring6.0.x jdk调成17 idea 2022.2.4版本本地编译spring源码项目 spring6.0.x 分支 gradle-8…

小程序-4(自定义组件:数据、属性、数据监听器、生命周期函数、插槽、父子通信、behaviors)

目录 1.组件的创建和引用 局部引用组件 全局引用组件 组件和页面的区别 组件样式隔离 ​编辑 组件样式隔离的注意点 修改组件的样式隔离选项 data数据 methods方法 properties属性 data和properties属性的区别 使用setData修改properties的值 2.数据监听器 什么…

stm32入门-----EXTI外部中断(下——实践篇)

目录 前言 一、硬件介绍 1.对射红外线传感器 2.旋转编码器 二、EXTI外部中断C编程 1.开启RCC时钟 2.配置GPIOK口初始化 3.配置AFIO 4.配置EXIT 5.配置NVIC 三、EXIT外部中断项目实操 1.对射红外传感器计数 2.选择编码器计数 前言 本期接着上一期的内容继续学习stm3…

AutoMQ 生态集成 Redpanda Console

通过 Kafka Web UI 更加便利地管理 Kafka/AutoMQ 集群 随着大数据技术的飞速发展&#xff0c;Kafka 作为一种高吞吐量、低延迟的分布式消息系统&#xff0c;已经成为企业实时数据处理的核心组件。然而&#xff0c;Kafka 集群的管理和监控却并非易事。传统的命令行工具和脚本虽…

Java流的概念及API

流的概念 流&#xff08;Stream)的概念代表的是程序中数据的流通&#xff0c;数据流是一串连续不断的数据的集合。在Java程序中&#xff0c;对于数据的输入/输出操作是以流(Stream)的方式进行的。可以把流分为输入流和输出流两种。程序从输入流读取数据&#xff0c;向输出流写入…

Python项目打包与依赖管理指南

在Python开发中&#xff0c;python文件需要在安装有python解释器的计算机的电脑上才能运行&#xff0c;但是在工作时&#xff0c;我们需要给客户介绍演示项目功能时并不一定可以条件安装解释器&#xff0c;而且这样做非常不方便。这时候我们可以打包项目&#xff0c;用于给客户…

《驾驭AI浪潮:伦理挑战与应对策略》

AI发展下的伦理挑战&#xff0c;应当如何应对&#xff1f; 人工智能飞速发展的同时&#xff0c;也逐渐暴露出侵犯数据隐私、制造“信息茧房”等种种伦理风险。随着AI技术在社会各个领域的广泛应用&#xff0c;关于AI伦理和隐私保护问题日趋凸显。尽管国外已出台系列法规来规范…

达梦数据库DM8-索引篇

目录 一、前景二、名词三、语法1、命令方式创建索引1.1 创建索引空间1.2.1 创建普通索引并指定索引数据空间1.2.2 另一种没验证&#xff0c;官方写法1.3 复合索引1.4 唯一索引1.5 位图索引1.6 函数索引 2、创建表时候创建索引3、可视化方式创建索引3.1 打开DM管理工具3.2 找到要…

nginx负载均衡实例

实现效果 浏览器输入地址http://nginx服务器ip(:80)/edu/a.html&#xff0c;实现负债均衡效果&#xff0c;平均分配到 服务器ip:8080和 服务器ip:8081进程中。 准备工作 准备两个tomcat&#xff0c;一个监听在8080端口&#xff0c;一个监听在8081端口。也可以准备多个tomcat。…

如何在电脑上演示手机上APP,远程排查移动端app问题

0序&#xff1a; 对接客户&#xff0c;给领导演示移动端产品&#xff0c;或者远程帮用户排查移动端产品的问题。都需要让别人能够看到自己在操作手机。 会议室可以使用投屏&#xff0c;但需要切换电脑和手机。 排查问题经常都是截图、或者手机上录制视频&#xff0c;十分繁琐…

node-red学习

Node-RED : 起步 1、安装nodejs Node.js — 在任何地方运行 JavaScript 验证 2、更换下载源 // 查看当前下载地址 npm config get registry // 设置淘宝镜像的地址 npm config set registry https://registry.npmmirror.com/ // 查看当前的下载地址 npm config get registry…