一个混合了Obj-C和C的iOS项目。我有一个大约1 MB大小的POD结构。它有一个全局示例。如果我在一个工作线程上调用的函数中创建一个相同的本地示例,复制操作在模拟器上的调试构建中崩溃(从工作线程调用时)。发布构建不会崩溃。
这感觉像是用完了堆栈大小。
有问题的工作线程不是手动创建的-是NSOperationQueue
工作线程。
问题有两个方面:
- 为什么自动堆栈增长失败?
- 如何增加
NSOperationQueue
线程上的堆栈大小?
录音如下:
struct S
{
char s[1024*1024];
};
S gs;
-(void)f
{
S ls;
ls = gs; //Crash!
}
好的,我看到了重写NSOperationQueue
来增加堆栈。也就是说-**编译器(Clang)肯定有某种解决方案。我将检查反汇编以防万一,但相同的代码在发布版本中不会崩溃。为什么它在这种情况下不起作用呢?OBTW,在Android上检查了相同的代码,看到了相同的崩溃。
2条答案
按热度按时间3phpmpom1#
我不知道这个文档有多新,但是根据苹果公司的说法,除非在线程创建过程中另有配置,否则非主线程的栈顶大小为512 kB。
我很难找到将这样的数据结构存储在堆栈上的好理由,特别是在(Obj-)C++中,您可以轻松地将其 Package 在类似
std::unique_ptr
的东西中,它可以自动管理堆分配和释放。(或者任何其他基于RAII的抽象,或者甚至将其存储为支持ARC的Objective-C类中的一个ivar,如果您愿意的话)。选择非常大的堆栈大小的一个缺点是,这些内存可能会一直驻留,但在线程终止之前不会被使用,特别是在iOS上,这些内存甚至不会被交换到磁盘。如果你显式地启动一个线程,并在完成需要巨大堆栈的算法后关闭它,这是很好的。但如果你在一个池线程上运行一次性作业,你现在实际上已经泄漏了1 MB的内存。也许是因为我是嵌入式开发人员(或者我记得iPhone只有128 MB内存的时候),但我不想写这样的代码。(或者有人能拿出证据证明低内存警告机制会清除未使用的堆栈空间吗?)
zaq34kh62#
Cocoa的线程机制使用unix POSIX线程,其堆栈大小遵循以下规则:
ulimit -s
命令找到此值,对于我的机器是8192
KiB,但对于iOS,很可能要小几倍)回答你的第一个问题:
它"失败"是因为它不允许增长到超过为给定线程分配的大小。在这种情况下更有趣的问题是-为什么它在发布版本中没有失败?坦白地说,我在这里没有答案。我假设它很可能与优化有关,在优化中允许编译器绕过某些内存流例程或完全丢弃某些代码部分。
对于第二个问题:
应用程序的主线程始终具有默认的系统堆栈大小,并且只能在macOS(或root iOS设备)中使用
ulimit
进行更改(大小以KiB为单位):iOS和macOS下的所有其他线程(据我所知)都有明确指定的大小,它等于512 KiB。您必须以某种方式将堆栈大小转发给
pthread_create(3)
函数,如下所示:不幸的是,队列API(
GCD
或NSOperation
)都没有公开线程池的分配部分,更不用说NSThread
不允许您显式地指定自己的pthread
用于底层执行。如果您想依赖这些API,您将不得不"人工"实现它。具有任意堆栈大小线程的
NSOperation
子类示例这样一个类的接口可能看起来像这样(假设线程的堆栈大小是恒定的,并且不应该是一个注入的依赖项):
实现文件:
现在,您可以像使用常规
NSOperation
示例一样使用它: