在dispatch_async中执行简单的malloc / free会导致iOS9上的内存泄漏

时间:2015-10-22 16:09:23

标签: ios objective-c memory-leaks ios9 grand-central-dispatch

在我将iPad更新到iOS9之后,我的代码中出现了内存泄漏,这在iOS8和iOS7上运行良好。

我有一个由以下代码创建的匿名线程:

dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_HIGH, 0), ^{
    [self threadWork];
});

并且该线程执行了一对malloc / free调用:

- (void)threadWork {
    // Create a serial queue.
    dispatch_queue_t mySerialQueue = dispatch_queue_create("myQueue", NULL);

    while (1) {
        // Do a simple malloc.
        int *foo = (int *)malloc(1024);

        // Do free in serial queue.
        dispatch_async(mySerialQueue, ^{
            free(foo);
        });

        [NSThread sleepForTimeInterval:1.0 / 60.0];
    }
}

此路由将增加内存使用量并最终崩溃iOS 9上的设备。在Objective-C ++中新问题/删除也会出现问题。

我发现了其他一些没有内存泄漏的方法:

  1. 使用主队列或全局队列代替串行队列。
  2. 创建并发队列而不是串行队列。
  3. 使用[NSThread detachNewThreadWithSelector:toTarget:withObject:]创建线程而不是GCD。
  4. 我不明白为什么这个简单的路由会导致这个问题。 我在谷歌上搜索了这个,但一无所获。

    如何保留串行队列和GCD匿名线程?

    更新

    我试图在我的代码中放置NSLog命令,以确定何时调用malloc / free。结果表明,它们都被立即调用并成对出现。我也尝试将线程减慢到每秒一次,但问题仍然存在。

    线程的测试代码:

    - (void)threadWork {
        uint64_t mallocCount = 0;
        __block uint64_t freeCount = 0;
        dispatch_queue_t mySerialQueue = dispatch_queue_create("MyQueue", NULL);
        while (1) {
            void *test = malloc(1024);
            NSLog(@"malloc %llu", ++mallocCount);
            dispatch_async(mySerialQueue, ^{
                free(test);
                NSLog(@"free %llu", ++freeCount);
            });
            [NSThread sleepForTimeInterval:1.0];
        }
    }
    

    控制台结果:

    ...
    2015-10-23 09:51:33.876 OS9MemoryTest[759:153135] malloc 220
    2015-10-23 09:51:33.876 OS9MemoryTest[759:153133] free 220
    2015-10-23 09:51:34.877 OS9MemoryTest[759:153135] malloc 221
    2015-10-23 09:51:34.878 OS9MemoryTest[759:153133] free 221
    2015-10-23 09:51:35.883 OS9MemoryTest[759:153135] malloc 222
    2015-10-23 09:51:35.883 OS9MemoryTest[759:153133] free 222
    

1 个答案:

答案 0 :(得分:1)

我认为我找到了一种更好的方法来做到这一点,而不是使用dispatch_sync。

这一点似乎是串行队列的服务质量(QoS)类的设置。 在具有QOS_CLASS_UNSPECIFIED QoS类的队列中空闲会导致此问题。


在我的问题中,我释放了一个由以下调用创建的串行队列中的内存:

dispatch_queue_t mySerialQueue = dispatch_queue_create("MyQueue", NULL);

其QoS设置为QOS_CLASS_UNSPECIFIED,导致此问题。

如果创建一个带有dispatch_queue_attr_t对象的串行队列,其QoS设置不包括QOS_CLASS_UNSPECIFIED,则代码运行完美而不会泄漏:

- (void)threadWork {
    // Create a serial queue with QoS class.
    dispatch_queue_attr_t attr = dispatch_queue_attr_make_with_qos_class(DISPATCH_QUEUE_SERIAL, QOS_CLASS_DEFAULT, 0);
    dispatch_queue_t mySerialQueue = dispatch_queue_create("myQueue", attr);

    while (1) {
        // Do a simple malloc.
        int *foo = (int *)malloc(1024);

        // Do free in serial queue.
        dispatch_async(mySerialQueue, ^{
            free(foo);
        });

        [NSThread sleepForTimeInterval:1.0 / 60.0];
    }
}

我仍然不明白为什么在iOS9上会出现这个问题, 但设置QoS似乎可以使事情发挥作用。