仅通过 Objective-C 运行时函数使用 NSAutoreleasePool

Using NSAutoreleasePool only via Objective-C runtime functions

我正在学习 Objective-C 中内存管理的工作原理。据我所知,标记为 autorelease 的对象将被添加到封闭的 NSAutoreleasePool 中,并在池为 released/drained.

时被释放

为了尝试我的新知识,我创建了一些测试;创建 100 万个对象使用了 ~17mb,创建相同的对象但立即释放它们使用了 ~1mb。但是,我无法让 NSAutoreleasePool 工作,使用下面的代码仍然使用 ~17mb,我不明白为什么。

#include <objc/objc-runtime.h>
#include <stdio.h>

int main(void) {
    Class NSAutoreleasePool = objc_getClass("NSAutoreleasePool");
    Class Object = objc_getClass("NSObject");
    SEL new = sel_registerName("new");
    SEL drain = sel_registerName("drain");
    SEL autorelease = sel_registerName("autorelease");
    id pool = objc_msgSend(NSAutoreleasePool, new);
    for (int i = 0; i < 1e6; i++) {
        id obj = objc_msgSend(Object, new);
        objc_msgSend(obj, autorelease);
    }
    objc_msgSend(pool, drain);
    printf("OK\n");
    getchar();
    return 0;
}

当您 release 每个对象在创建后立即创建时,一次存在的对象不会超过一个。所以运行时间可以为每个新对象回收相同的内存。 运行时间只需要向操作系统请求足够的内存来容纳那个对象。

当您 autorelease 创建每个对象后,每个对象都会存在,直到自动释放池被耗尽,因此您最终会同时存在 1,000,000 个对象。 运行时间必须向操作系统请求足够的内存来容纳所有对象。由于 运行time 事先并不知道您打算创建多少个对象,因此它会逐渐向操作系统请求内存块。也许它要求 1 MiB,当 1 MiB 用完时,它要求另外 1 MiB,依此类推。

通常,当您的程序释放一些内存时(在本例中是因为它破坏了一个对象),运行time 不会 return 将内存分配给操作系统。它使内存可用,以便下次创建对象时可以重用它。

操作系统知道它为您的进程分配了多少内存,但它不知道该内存的哪些部分(在进程内)被视为“正在使用”,哪些部分被视为“空闲”。

Activity Monitor 和 top 都向操作系统询问有关您的进程的信息。所以他们只知道操作系统知道的,也就是给你的进程分配了多少内存。他们不知道有多少内存在使用中,有多少是空闲的。

如果您想更准确地了解程序中活动对象使用了多少内存,则需要使用侵入性更强的工具。 Xcode 附带的 Instruments 程序可以使用 Allocations 工具显示“实时分配”使用了多少内存。

这是分配工具显示的内容,当我 运行 你的程序在它下面大约三秒钟时:

所以你可以看到分配工具检测到总共 1,001,983 个“瞬态”分配。临时分配是在 Instruments 停止记录之前分配并释放的内存块。

您还可以看到分配的“持久”字节(即 Instruments 停止记录时已分配且未释放的内存)为 506 KiB,但分配的总字节(包括后来释放的字节)为 23.5 MiB。