10

以下代码将占用 ~410MB 内存,不会再次释放。(使用dispatch_sync代替的版本dispatch_async将需要约 8MB 内存)
我预计会出现高内存使用高峰,但它应该会再次下降......泄漏在哪里?

int main(int argc, const char * argv[]) {
  @autoreleasepool {
    for (int i = 0; i < 100000; i++) {
      dispatch_async(dispatch_get_global_queue(QOS_CLASS_UTILITY, 0), ^{
        NSLog(@"test");
      });
    }
    NSLog(@"Waiting.");
    [[NSRunLoop mainRunLoop] runUntilDate:[NSDate dateWithTimeIntervalSinceNow:60]];
  }
  return 0;
}  

我试过了:

  • 在循环周围和内部添加@autoreleasepool
  • 添加NSRunLoop run到循环

我尝试了几种组合,但从未发现内存减少(即使等待几分钟后)。我知道包含以下语句的 GCD 参考指南:

尽管 GCD 调度队列有自己的自动释放池,但它们不能保证这些池何时耗尽。

此代码中是否存在内存泄漏?如果没有,有没有办法强制队列释放/排出完成的块?

4

2 回答 2

1

Objective-C 块它是一个 C 结构,我认为你创建了 100000 个块对象以在后台线程中执行它们,它们等待系统可以运行它们。您的设备可以执行有限数量的线程,这意味着在操作系统启动它们之前会等待许多块。

如果将“async”更改为“sync”,则会在前一个块完成并销毁后创建下一个块对象。

UPD

关于 GCD 池。

GCD 在 GCD 线程池上执行任务,线程由系统创建,由系统管理。系统缓存线程以节省 CPU 时间,每个调度任务都在空闲线程上执行。

从文档:

——</p>

提交到调度队列的块在系统完全管理的线程池上执行。不保证执行任务的线程。

——</p>

如果您将任务作为同步任务运行,则存在空闲线程(来自 GCD 线程池)来执行下一个任务,在当前任务完成后(因为主线程在任务执行时正在等待,并且不会将新任务添加到队列中),并且系统没有分配新的 NSThread (在我的 Mac 上我看到了 2 个线程)。如果您将任务作为异步运行,那么系统可以分配许多 NSThreads(为了获得最大性能,在我的 mac 上它接近 67 个线程),因为全局队列包含许多任务。

在这里您可以了解 GCD 线程池的最大计数。

我在 Alocations profiler 中看到有许多 NSThreads 被分配并且没有被破坏。我认为这是系统池,如有必要,将被释放。

于 2015-02-16T09:16:28.553 回答
1

始终将 @autoreleasepool 放在每个 GCD 调用中,您不会有任何问题。我有同样的问题,这是唯一的解决方法。

int main(int argc, const char * argv[]) {
  @autoreleasepool {
    for (int i = 0; i < 100000; i++) {
      dispatch_async(dispatch_get_global_queue(QOS_CLASS_UTILITY, 0), ^{
        // everything INSIDE in an @autoreleasepool
        @autoreleasepool {
          NSLog(@"test");
        }
      });
    }
    NSLog(@"Waiting.");
    [[NSRunLoop mainRunLoop] runUntilDate:[NSDate dateWithTimeIntervalSinceNow:60]];
  }
  return 0;
}  
于 2016-10-01T12:12:44.947 回答