iOS - AutoreleasePool

1. 基本数据结构

// AutoreleasePool 的基本结构
struct AutoreleasePoolPage {
    static pthread_key_t const key = AUTORELEASE_POOL_KEY;
    magic_t const magic;
    id *next;               // 指向下一个可存放对象的地址
    pthread_t const thread; // 所属线程
    AutoreleasePoolPage * const parent;  // 双向链表结构
    AutoreleasePoolPage *child;
    uint32_t const depth;
    uint32_t hiwat;
    
    // 大小固定为 4096 字节(一个虚拟内存页的大小)
    static size_t const SIZE = PAGE_MAX_SIZE;
}

2. 存储结构

class AutoreleasePoolPage {
    AutoreleasePoolPage * const parent;
    AutoreleasePoolPage *child;
    
    // 存储空间
    id *begin() { return (id *) ((uint8_t *)this+sizeof(*this)); }
    id *end() { return (id *) ((uint8_t *)this+SIZE); }
}

2.2 哨兵对象

#define POOL_BOUNDARY nil     // 池边界标记
static inline void *push() {
    id *dest = autoreleaseFast(POOL_BOUNDARY);
    return dest;
}

3. 线程关系

3.1 TLS存储

// 每个线程都有自己的 AutoreleasePool 栈
static pthread_key_t key;
static void tls_dealloc(void *p) {
    if (p == (void*)EMPTY_POOL_PLACEHOLDER) return;
    AutoreleasePoolPage *page = (AutoreleasePoolPage*)p;
    page->kill();  // 清理操作
}

3.2 线程本地存储初始化

void tls_init() {
    _objc_pthread_key = pthread_key_create(&_objc_pthread_destroyspecific);
    AutoreleasePoolPage::key = tls_create(&_objc_autoreleasepool_deallocate);
}

4. 对象管理

4.1 添加对象

static inline id *autoreleaseFast(id obj) {
    AutoreleasePoolPage *page = hotPage();
    if (page && !page->full()) {
        return page->add(obj);
    } else if (page) {
        return autoreleaseFullPage(obj, page);
    } else {
        return autoreleaseNoPage(obj);
    }
}

4.2 清理对象

void releaseAll() {
    // 释放所有autorelease对象
    AutoreleasePoolPage *page = this;
    while (page->child) page = page->child;
    
    do {
        page->releaseUntil(page->begin());
    } while ((page = page->parent));
}

5. 生命周期管理

5.1 创建时机

// 1. 显式创建
@autoreleasepool {
    // 代码块
}

// 2. RunLoop 相关
void _wrapRunLoopWithAutoreleasePool(void) {
    @autoreleasepool {
        // RunLoop 逻辑
    }
}

5.2 释放时机

// 1. @autoreleasepool 块结束
{
    void *token = objc_autoreleasePoolPush();
    // ... 
    objc_autoreleasePoolPop(token);
}

// 2. RunLoop 迭代结束
void _runLoopIterationDidEnd() {
    objc_autoreleasePoolPop(currentPool);
}

6. 内存管理

6.1 页面管理

void *push() {
    id *dest;
    if (DebugPoolAllocation) {
        // ...debug code...
    }
    dest = autoreleaseFast(POOL_BOUNDARY);
    return dest;
}

static inline void pop(void *token) {
    AutoreleasePoolPage *page;
    id *stop;
    page = pageForPointer(token);
    stop = (id *)token;
    page->releaseUntil(stop);
}

6.2 溢出处理

static id *autoreleaseFullPage(id obj, AutoreleasePoolPage *page) {
    // 当前页满了,创建新页
    AutoreleasePoolPage *next = new AutoreleasePoolPage(page);
    page->child = next;
    return next->add(obj);
}

7. 特殊情况处理

7.1 嵌套池

@autoreleasepool {           // 外层池
    NSString *str1 = @"1";
    @autoreleasepool {       // 内层池
        NSString *str2 = @"2";
    }                        // str2 被释放
}                           // str1 被释放

7.2 异常处理

void unwinding_cleanup() {
    // 异常展开时确保 AutoreleasePool 正确清理
    AutoreleasePoolPage *page = hotPage();
    if (page) {
        pop(page->begin());
    }
}

8. 性能优化

8.1 快速路径

static inline id autorelease(id obj) {
    ASSERT(obj);
    id *dest __unused = autoreleaseFast(obj);
    return obj;
}

8.2 空间优化

// 使用一页虚拟内存(4KB)作为存储单元
#define PAGE_MAX_SIZE 4096

9. 最佳实践

9.1 手动管理

// 处理大量临时对象
for (int i = 0; i < largeNumber; i++) {
    @autoreleasepool {
        // 创建临时对象的代码
    }
}

9.2 子线程处理

dispatch_async(dispatch_get_global_queue(0, 0), ^{
    @autoreleasepool {
        // 子线程代码
    }
});

10. 注意事项

1. 内存峰值:

// 不好的做法
for (int i = 0; i < largeNumber; i++) {
    // 创建autorelease对象
}  // 在循环结束才释放

// 好的做法
for (int i = 0; i < largeNumber; i++) {
    @autoreleasepool {
        // 创建autorelease对象
    }  // 每次迭代都释放
}

2. 线程安全:

// 每个线程维护自己的autoreleasepool栈
+ (void)threadEntryPoint {
    @autoreleasepool {
        // 线程代码
    }
}

3. RunLoop关系:

// RunLoop每次迭代都会创建和释放autoreleasepool
void runLoopMain() {
    while (running) {
        @autoreleasepool {
            // 一次RunLoop迭代
            runLoopIteration();
        }
    }
}

这里涵盖了AutoreleasePool 的主要实现细节和使用注意事项。理解这些内容对于正确使用AutoreleasePool和优化内存管理非常重要。

总结说明:

1. 每个AutoreleasePoolPage 存储了一个静态变量key和成员变量thread
2. 多线程共用一个 key 用于从 TLS 中获取当前线程的的 page 实例对象
3. 在 TLS 内部获取当前的线程,使用 key 从当前线程的一个哈希表中取出对应的 page
4. 一个线程可能有多个 page,TLS 存储双向链表中当前线程对应的最后一个page和key之间的映射
5. 成员变量 thread 用于判断遍历过程中的 page节点是否是当前线程的page,如果不是则跳过,向前跳一个节点(page)

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

SSR 【1】【nuxt安装】

文章目录 前言如何解决 前言 nuxt提供了nuxi脚手架工具&#xff0c;让开发者便捷生成nuxt模板项目。nuxt官网 npx nuxilatest init <project-name>但是几乎大部分的人在安的时候都会遇到这个问题 如何解决 在C:\Windows\System32\drivers\etc\hosts中增加如下解析记录…

性能测试05|JMeter:分布式、报告、并发数计算、性能监控

目录 一、JMeter分布式 1、应用场景 2、原理 3、分布式相关注意事项 4、分布式配置与运行 二、JMeter报告 1、聚合报告 2、HTML报告 三、并发用户数&#xff08;线程数&#xff09;计算 四、JMeter下载第三方插件 五、性能监控 1、Concurrency Thread Group 线程组…

CURSOR 应用:深入理解字符前缀条件算法(Character Prefix Conditioning)

前言 在代码补全中&#xff0c;用户期待智能模型能根据输入快速、准确地给出建议。但现代语言模型基于Token序列运作&#xff0c;这在处理非Token边界输入时会带来偏差。为了解决这一问题&#xff0c;本文将探讨一种高效算法——字符前缀条件算法&#xff08;Character Prefix…

滤波器设计流程

sos滤波器是什么为什么要 zpk2sos如何实现零相位滤波&#xff0c;优缺点分别是什么 滤波器的计算流程 滤波器的计算设计流程&#xff1a; 1.输入验证和处理&#xff1a; 2.检查频率范围是否合法&#xff0c;计算归一化的频率。 3.滤波器设计&#xff1a;设计带通 Butterworth…

【游戏设计原理】53 - 解决问题的障碍

1. 分析并总结原理 核心观点 游戏本质是一系列问题解决的过程&#xff0c;通过设计巧妙的问题和决策场景&#xff0c;游戏能激发玩家的兴趣和投入感。然而&#xff0c;当问题解决的过程被阻碍时&#xff0c;会降低玩家的体验甚至让他们放弃游戏。文中提到的四种障碍反映了玩家…

【多线程初阶篇¹】线程理解| 线程和进程的区别

目录 一、认识线程Thread 1.为啥引入线程 2.线程理解 &#x1f525; 3.面试题&#xff1a;线程和进程的区别 一、认识线程Thread 1.为啥引入线程 为了解决进程太重量的问题 解释&#xff08;为什么说线程比进程更轻量&#xff1f;/为什么说线程创建/销毁开销比进程小&#…

平面坐标转大地坐标(arcgisPro中进行)

1、将需要转换的红线导入arcgisPro中&#xff0c;如下&#xff1a; 2、在地图菜单栏中&#xff0c;选择坐标转换工具&#xff0c;如下&#xff1a; 3、打开坐标转换工具 4、开启捕捉 5、 设置大地坐标显示格式 6、如下&#xff1a; 7、显示如图&#xff1a; 8、再依次添加几个待…

CentOS: RPM安装、YUM安装、编译安装(详细解释+实例分析!!!)

目录 1.什么是RPM 1.1 RPM软件包命名格式 1.2RPM功能 1.3查询已安装的软件&#xff1a;rpm -q 查询已安装软件的信息 1.4 挂载&#xff1a;使用硬件&#xff08;光驱 硬盘 u盘等&#xff09;的方法&#xff08;重点&#xff01;&#xff01;&#xff01;&#xff09; 1…

n8n - AI自动化工作流

文章目录 一、关于 n8n关键能力n8n 是什么意思 二、快速上手 一、关于 n8n n8n是一个具有原生AI功能的工作流自动化平台&#xff0c;它为技术团队提供了代码的灵活性和无代码的速度。凭借400多种集成、原生人工智能功能和公平代码许可证&#xff0c;n8n可让您构建强大的自动化…

GWAS数据和软件下载

这部分主要是数据获取,以及软件配置方法。 一、配套数据和代码 数据和代码目前在不断的更新,最新的教程可以私信,我通过后手动发送最新版的pdf和数据代码。发送的压缩包,有电子版的pdf和数据下载链接,里面是最新的百度网盘的地址,下载到本地即可。然后根据pdf教程,结合配套的…

maven多模块项目编译一直报Failure to find com.xxx.xxx:xxx-xxx-xxx:pom:1.0-SNAPSHOT in问题

工作中项目上因为多版本迭代&#xff0c;需要对不同迭代版本升级版本号&#xff0c;且因为项目工程本身是多模块结构&#xff0c;且依然多个其他模块工程。 在将工程中子模块的pom.xml中版本号使用变量引用父模块中定义的版本号时&#xff0c;一直报Failure to find com.xxx.x…

STM32 I2C硬件配置库函数

单片机学习&#xff01; 目录 前言 一、I2C_DeInit函数 二、I2C_Init函数 三、I2C_StructInit函数 四、I2C_Cmd函数 五、I2C_GenerateSTART函数 六、I2C_GenerateSTOP函数 七、I2C_AcknowledgeConfig函数 八、I2C_SendData函数 九、I2C_ReceiveData函数 十、I2C_Sen…

JavaEE初阶——计算机工作原理

一、什么是JavaEE JavaEE&#xff08;Java Platform&#xff0c;Enterprise Edition&#xff09;是sun公司&#xff08;2009年4月20日甲骨文将其收购&#xff09;推出的企业级应用程序版本。这个版本以前称为 J2EE。能够帮助我们开发和部署可移植、健壮、可伸缩且安全的服务器…

【微服务】2、网关

Spring Cloud微服务网关技术介绍 单体项目拆分微服务后的问题 服务地址问题&#xff1a;单体项目端口固定&#xff08;如黑马商城为8080&#xff09;&#xff0c;拆分微服务后端口各异&#xff08;如购物车808、商品8081、支付8086等&#xff09;且可能变化&#xff0c;前端难…

【JAVA】Java开发小游戏 - 简单的2D平台跳跃游戏 基本的2D平台跳跃游戏框架,适合初学者学习和理解Java游戏开发的基础概念

前些天发现了一个巨牛的人工智能学习网站&#xff0c;通俗易懂&#xff0c;风趣幽默&#xff0c; 忍不住分享一下给大家。点击跳转到网站 学习总结 1、掌握 JAVA入门到进阶知识(持续写作中……&#xff09; 2、学会Oracle数据库入门到入土用法(创作中……&#xff09; 3、手把…

【学Rust开发CAD】1 环境搭建

文章目录 一、搭建C/C编译环境二、安装Rust三、配置 PATH 环境变量四、验证安装结果五、安装编辑工具 一、搭建C/C编译环境 Rust 的编译工具依赖 C 语言的编译工具&#xff0c;这意味着你的电脑上至少已经存在一个 C 语言的编译环境。如果你使用的是 Linux 系统&#xff0c;往…

【HTML】Day02

【HTML】Day02 1. 列表标签1.1 无序列表1.2 有序列表1.3 定义列表 2. 表格标签2.1 合并单元格 3. 表单标签3.1 input标签基本使用3.2 上传多个文件 4. 下拉菜单、文本域5. label标签6. 按钮button7. div与span、字符实体字符实体 1. 列表标签 作用&#xff1a;布局内容排列整齐…

中国科技统计年鉴EXCEL版(2021-2023年)-社科数据

中国科技统计年鉴EXCEL版&#xff08;2021-2023年&#xff09;-社科数据https://download.csdn.net/download/paofuluolijiang/90028724 https://download.csdn.net/download/paofuluolijiang/90028724 中国科技统计年鉴提供了从2021至2023年的详尽数据&#xff0c;覆盖了科技…

[Linux]Mysql9.0.1服务端脱机安装配置教程(redhat)

前言 本教程适用于在yum源不可用的LInux主机上安装Mysql的场景。 以redhat系主机做操作示例&#xff0c;debian系主机可参照步骤&#xff0c;将对应的rpm -ivh命令换成dpkg -i。 1. 官网下载安装包 https://dev.mysql.com/downloads/mysql/ 1.1 版本分类 MySQL Enterprise…

Apache Paimon-实时数据湖

一、Apache Paimon是什么? Flink社区希望能够将 Flink 的 Streaming 实时计算能力和 Lakehouse 新架构优势进一步结合&#xff0c;推出新一代的 Streaming Lakehouse 技术&#xff0c;促进数据在数据湖上真正实时流动起来&#xff0c;并为用户提供实时离线一体化的开发体验。 …