【问题标题】:How to reproduce a rare "_CFAutoReleasePoolPop" crash?如何重现罕见的“_CFAutoReleasePoolPop”崩溃?
【发布时间】:2020-02-24 02:51:48
【问题描述】:

我正在尝试重现此类崩溃:

我的项目中有手动引用计数。此外,还有很多多线程。 有些属性不是线程安全的。 :(
我对这次崩溃的原因只有一个假设:某些对象被过度释放(?)

我添加了自动化 UI 测试 (Appium),但它们还没有帮助。
另外,我已经为Zombies 进行了分析 - 一切似乎都很好。
另外,我试过 Xcode 的静态分析器(Product -> Analyze),有很多警告,但似乎没有一个是导致这种崩溃的原因(我看过警告Incorrect decrement of reference count not owned at this point)。

我用 MRC 创建了一个测试项目,并添加了这样的代码:

- (void)testAssumptions {
    //@autoreleasepool
    {
        [self overReleaseNilValue];
        [self overReleaseNotNilValue];
    }
}

- (void)overReleaseNilValue {
    NSIndexPath* path = [[NSIndexPath alloc] initWithIndex:42];
    [path release];
    [path release];
}

- (void)overReleaseNotNilValue {
    NSIndexPath* path = nil;
    [path release];
    [path release];
}

在启用自动释放池或没有池的情况下,两次释放对象都不会崩溃。

所以我的问题是:
1. 除了释放已经释放的对象之外,这种崩溃的另一个原因是什么?
2. 有没有办法增加重现此类崩溃的概率?例如。一些环境。变量会降低一些自动释放池对不安全代码的容忍度?或者一些额外的自动释放池?
3. 为什么我的测试项目代码没有崩溃?

任何cmets都非常感谢。

【问题讨论】:

    标签: ios objective-c core-foundation nsautoreleasepool manual-retain-release


    【解决方案1】:

    此时不拥有的引用计数的错误减少

    这绝对是值得探索的警告。几乎可以肯定,它至少会指出一个错误。

    您的测试项目实际上并没有测试任何东西(我相信它们也被倒过来命名)。没有保证过度释放一个值会导致崩溃。 overReleaseNotNilValue 是明确定义的行为,绝对不会崩溃(向nil 发送消息什么都不做)。 overReleaseNilValue 是未定义的行为。我还没有深入研究它,但我希望NSIndexPath 可以使用标记指针来实现,如果你过度释放它们也不会崩溃。

    未定义就是未定义。这并不意味着崩溃。过度释放一个值可以做任何事情。如果你幸运的话,它会崩溃....

    此外,还有很多多线程。有些属性不是线程安全的。

    如果它是间歇性的,我希望这是您问题的核心。我从事过这样的项目。解决方案是解决问题。您将不知道导致崩溃的具体问题。你可能永远不会知道。你仍然必须修复它们。这需要一些时间,但您必须使代码线程安全,否则其行为未定义。

    至于调试,您需要按顺序执行以下操作:

    • 打开运行时清理选项(在方案编辑器下,运行,诊断)。为此,您尤其需要 Thread Sanitizer。

    • 清除所有静态分析器警告。如果他们中的任何人说您的内存管理有误,您必须清除这些。该系统实际上是在告诉您问题出在哪里。不要忽视它。

    • 清除所有警告。您的项目中应该有零警告。如果有很多“错误”警告,那么您将永远不会看到真正的警告告诉您问题出在哪里。消除所有警告。

    我花了 8 个月的时间在一个由专家开发人员编写的精心编写的项目中消除了罕见的过度发布崩溃,并且几乎没有线程。这可能需要很多时间。你必须清除每一个问题。一个不正确的版本就足以让程序随机崩溃。

    【讨论】:

    • 非常感谢,这看起来是一个非常明智的答案! :) 至于Thread Sanitizer,我希望如果我在代码中发现一些可疑的地方,我会用一些sleep func 强调它们,从而增加重现的概率。有意义吗?
    • TSan(线程清理器)实际上比这要聪明得多。它不会等待访问实际上具有竞争条件。它通过查找同步操作发生的位置来检测访问可能存在竞争条件的事实,并将其与内存访问进行比较。代码运行的速度有多快或多慢,或者即使它并行运行(即实际涉及多少个内核)都无关紧要。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2016-06-13
    • 2019-04-11
    • 1970-01-01
    • 2016-12-23
    • 1970-01-01
    • 2020-08-29
    • 1970-01-01
    相关资源
    最近更新 更多