自动释放的对象被释放,仍然耗尽内存?

时间:2012-07-01 06:31:57

标签: objective-c memory-management automatic-ref-counting instruments

http://i46.tinypic.com/2nquzag.png

我有一个应用程序,可以在很长的计算过程中创建并释放数百万个NSNumber。我正在使用@autoreleasepools来创建和处理这些对象的各种循环。正如您可以通过仪器屏幕上看到的那样,我只有10656个生活CFNumbers,但此时应用程序因为无法为其他对象分配内存而崩溃。 “暂时”状态下有超过5700万CFNumber。这是否意味着他们被释放了?为什么malloc还没有使用这些内存位置?

我已经包含了我正在使用的代码的模型。运行testProbabilities方法,它将计算每10000次迭代。在发生alloc错误之前,我使用此代码进行了790000次迭代。仪器看起来很好。

@implementation MMTest

NSMutableSet *predictedSet;


-(NSMutableArray *)calculateCDF {

//// NORMALIZE PROBABILITIES ////

float probabilitySum = 0;
float runningCDFTotal = 0;
float normaledProbability = 0;

@autoreleasepool {

    NSMutableArray *CDF = [[NSMutableArray alloc] init];

    probabilitySum = 4.5; ////This is just a placholder for additive iteration of an array which creates this sum.

    //// CREATE CDF ////

    int x;

    for (x=0; x<50; x++) {

        normaledProbability = .2/probabilitySum;////This is placeholder for calculation on same array as above.
        runningCDFTotal += normaledProbability;

        [CDF addObject:[NSNumber numberWithFloat:runningCDFTotal]];

    }

    return CDF;

}

}

-(NSMutableSet *)generateNumbers {

@autoreleasepool {

    int x;
    double r = 0;

    if (!predictedSet) {
        predictedSet = [[NSMutableSet alloc] init];
    }

    else {
        [predictedSet removeAllObjects];
    }

    for (x=0; x<5;) {

        r = arc4random_uniform(1000)/1000;////I'm actually using SFMT here instead.
        int num = 0;
        __weak id CDF = [self calculateCDF];


        if (r <= [[CDF objectAtIndex:[CDF count]-1] floatValue]) {

            for (NSNumber *cdf in CDF) {

                if (r <= [cdf floatValue]) {

                    num = [CDF indexOfObject:cdf]+1;

                    [predictedSet addObject:[NSNumber numberWithInt:num]];

                    x++;

                    break;


                }

            }

        }

    }

    return predictedSet;

}

}

-(void)testProbability {

int x = 0;
BOOL nextSetFound = NO;
NSSet *nextSet = [[NSSet alloc] initWithObjects:
                  [NSNumber numberWithInt:1],
                  [NSNumber numberWithInt:2],
                  [NSNumber numberWithInt:3],
                  [NSNumber numberWithInt:4],
                  [NSNumber numberWithInt:5],
                  nil];

while (nextSetFound == NO) {

    @autoreleasepool {

        __weak id newSet = [self generateNumbers];

        x++;

        if ([nextSet isSubsetOfSet:newSet]) {

            nextSetFound = YES;
            NSLog(@"%@", newSet);

        }   

        if (fmod (x,10000) == 0) {
            NSLog(@"%i", x);

        }

    }

}

NSLog(@"%i", x);

}

@end

2 个答案:

答案 0 :(得分:1)

我错了还是你没有在任何地方发布CDF?这样,每次拨打CDF时,您都会泄露calculateCDF

试试这个:

id CDF = [self calculateCDF];

而不是

 __weak id CDF = [self calculateCDF];

id newSet = [self generateNumbers];

而不是

__weak id newSet = [self generateNumbers];

指定weak会使对象不归newSet所有,因此不会由ARC自动释放。

另一方面,Apple explicitly adives against using __weak for stack variables

在堆栈上使用__weak变量时要小心。请考虑以下示例:

       NSString * __weak string = [[NSString alloc] initWithFormat:@"First Name: %@", [self firstName]];
       NSLog(@"string: %@", string);
     

虽然在初始赋值后使用了字符串,但在赋值时没有对字符串对象的其他强引用;因此,它立即被解除分配。 log语句显示string的值为null。 (如果你使用NSString * __weak string = [[NSString alloc] initWithString:@"First Name"],你将看不到效果。在这种情况下,initWithString:只返回永远不会被释放的字符串常量。)

据我了解此段落,您要么立即释放,要么发生内存泄漏。

我在iOS 4.3 iPhone上尝试了你的代码(删除了__weak),它在乐器下显示了一个扁平的“物理自由记忆”。

老答案:

autorelease的棘手问题是自动释放池在不可预测的时间耗尽(通常在应用程序执行运行循环时)。这与使用release不同,因为它在您完成对象的时刻与释放内存的时刻之间会增加一些延迟。

如果将这个小延迟(如autorelease系统所具有的惯性)乘以自动释放对象的数量,则可能导致内存使用高峰。如果iOS在其中一个峰值期间检查内存使用情况,并且您的应用无法对随后发生的内存警告做出正确反应,那么您的应用就会被杀死。

另一方面,如果你分配一个发布池,并且它在运行周期的同一步骤中变得很大,这也可能导致过多的内存使用。

你提供的代码细节很少,所以我不能更精确;无论如何,如果可能的话,我会尝试使用明确的release个对象。这将摆脱上述两个问题。或者您可以尝试处理较小的自动释放池(并且您将解决第二类问题)...

我还建议你为仪器添加一个内存监视器,这样你就可以看到系统释放内存的速度有多快。

PS:现在,你所附的图片让我印象深刻的是分配图是绝对平坦的:没有分配,没有解除分配。我希望内存能够上下移动(根据我的经验,这是一个健康的iOS应用程序的标志)。

答案 1 :(得分:0)

不确定原因,但上面这个确切的代码现在正常工作。在测试各种修改的过程中,我启用并禁用了Xcode中的Guard Malloc和Malloc Stack Logging。突然之间我的修改版本开始运行而没有崩溃。然后我再次尝试了这个原始版本,现在它运行良好而不会崩溃。这没有任何意义,但我很高兴它正在发挥作用。

@sergio:我想提出你的答案,至少提供很多帮助和建议,以进一步优化这些代码,但我没有足够的声誉。谢谢您的帮助。我很感激。