为什么这个 for 循环会耗尽内存?

Why does this for loop bleed memory?

我正在为我的 iOS 项目使用 ARC,并使用名为 SSKeychain 的库将 access/save 项目添加到钥匙串。我希望我的应用程序在峰值负载时每 10 秒左右访问一次钥匙串项(以访问 API 安全令牌),因此我想测试这个库以了解它在频繁调用时如何处理。我做了这个循环来模拟大量的调用,并注意到当 运行 在 iPhone(不是模拟器)上时它会消耗大量(~75 mb)内存:

- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {
    dispatch_async(dispatch_get_main_queue(), ^{
        NSUInteger beginMemory = available_memory();
        for (int i = 0; i < 10000; ++i) {

            @autoreleasepool{
                NSError *  error2 = nil;
                SSKeychainQuery*  query2 = [[SSKeychainQuery alloc] init];
                query2.service = @"Eko";
                query2.account = @"loginPINForAccountID-2";
                query2.password = nil;
                [query2 fetch:&error2];
            }
        }
        NSUInteger endMemory = available_memory();

        NSLog(@"Started with %u, ended with %u, used %u", beginMemory, endMemory, endMemory-beginMemory);
    });

    return YES;
}

static NSUInteger available_memory(void) {
    // Requires #import <mach/mach.h>
    NSUInteger result = 0;
    struct task_basic_info info;
    mach_msg_type_number_t size = sizeof(info);
    if (task_info(mach_task_self(), TASK_BASIC_INFO, (task_info_t)&info, &size) == KERN_SUCCESS) {
        result = info.resident_size;
    }
    return result;
}

我正在使用可以找到的 SSKeychain here。这个测试会消耗大约 75 MB 的内存,不管钥匙链上是否真的存储了东西。

知道发生了什么事吗?我的测试方法有缺陷吗?

您应该使用 Instruments 找出 where/what 导致泄漏的原因。很不错的工具,知道怎么用。

这篇文章有点过时,但您应该了解基本要点。

Ray Wenderlich - Instruments

离开 Paulw11 的 评论我偶然发现了这个,

From NSAutoreleasePool Class Reference:

The Application Kit creates an autorelease pool on the main thread at the beginning of every cycle of the event loop, and drains it at the end, thereby releasing any autoreleased objects generated while processing an event.

因此,当您使用仪器检查它时,请确保事件循环有时间完成。也许您需要做的就是让程序保持 运行 然后暂停调试器并再次检查仪器。

我 运行 你的代码在 Leaks Instrument 下,这是我从 Allocations track 看到的 -

这是您所期望的 - 在循环期间分配了大量内存,然后释放。

看你看到的细节-

2.36MB 堆上的持久字节 - 这是应用程序实际使用的内存 'now'(即在应用程序循环后 'idling')

8,646 个持久对象 - 同样,分配的对象数 "now"。

T运行sient objects 663,288 - 在应用程序生命周期内在堆上创建的对象总数。从t运行sient和persistent的区别可以看出大部分已经发布了

总字节数 58.70MB - 这是执行期间分配的内存总量。不是使用中的内存总量,而是已分配的内存总量,无论这些分配是否随后被释放。

浅粉色和深粉色条之间的差异也显示了当前 'active' 内存使用量与总使用量之间的差异。

您还可以从“泄漏检查”轨道中看到未检测到泄漏。

因此,总而言之,您的代码使用了很多 t运行sient 内存,正如您对紧密循环所期望的那样,但您不会在应用程序执行的正常过程中看到这种内存使用每秒或每分钟或其他任何时间访问钥匙串几次。

现在,我可以想象,在努力增加堆以支持所有这些对象后,iOS 不会立即将现在释放的堆内存释放回系统;您的应用程序稍后可能需要一个大堆 space,这就是为什么您的代码报告大量内存正在使用以及为什么您应该警惕尝试构建自己的检测而不是使用可用的工具。