Освобожденные объекты освобождены, все еще не хватает памяти?

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

У меня есть приложение, которое создает и выпускает миллионы номеров NSN в течение очень длительного расчета. Я использую @autoreleasepools с различными циклами, которые создают и обрабатывают эти объекты. Как вы можете видеть на экране инструментов, у меня есть только 10656 живых CFNumbers, но в этот момент приложение вылетает из-за невозможности выделить память для других объектов. В "переходном" состоянии более 57 миллионов номеров CFN. Значит ли это, что они освобождены? Почему malloc еще не использует эти области памяти?

Я включил макет кода, который я использую. Запустите метод testProbabilities, и он будет считать каждые 10000 итераций. Я получаю до 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 ответа

Решение

Не знаю точно, почему, но этот точный код выше теперь отлично работает как есть. В ходе тестирования различных модификаций я включил и отключил Guard Malloc и Malloc Stack Logging в Xcode. Внезапно мои модифицированные версии начали работать без сбоев. Затем я снова попробовал эту оригинальную версию, и теперь она работает нормально, без сбоев. Это не имеет смысла, но я просто рад, что это работает.

@sergio: Я хотел бы поддержать ваш ответ, по крайней мере, за то, что он предоставил большую помощь и советы для дальнейшей оптимизации этого кода, но у меня недостаточно высокая репутация. Спасибо за помощь. Я ценю это.

Я не прав или вы никуда не выпускаете 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 явно выступает против использования __weak для переменных стека:

Будьте осторожны при использовании переменных __weak в стеке. Рассмотрим следующий пример:

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

Хотя строка используется после начального присваивания, во время присваивания нет другой сильной ссылки на строковый объект; поэтому он немедленно освобождается. Оператор log показывает, что строка имеет нулевое значение. (Вы не увидите эффект, если вы используете, например, NSString * __weak string = [[NSString alloc] initWithString:@"First Name"], В этом случае, initWithString: просто возвращает строковую константу, которая никогда не освобождается.)

Насколько я понимаю этот параграф, у вас либо немедленное освобождение, либо утечка памяти.

Я попробовал ваш код на iOS 4.3 iPhone (без __weak), и он показывает плоскую "физическую свободную память" под инструментами.

СТАРЫЙ ОТВЕТ:

Хитрая вещь с autorelease заключается в том, что пулы авто-выпуска очищаются в непредсказуемое время (обычно, когда приложение выполняет цикл выполнения). Это отличается от использования release в том, что это добавляет некоторую задержку между моментом, когда вы закончили с объектом, и моментом, когда память освобождается.

Если вы умножите эту небольшую задержку (как инерция autorelease система имеет) по количеству автоматически выпущенных объектов, это может привести к пикам использования памяти. Если iOS проверяет использование памяти во время одного из этих пиков, и ваше приложение не может должным образом отреагировать на следующее предупреждение о памяти, то ваше приложение уничтожается.

С другой стороны, если вы выделите пул релизов, и он станет огромным на одном и том же этапе цикла выполнения, это также может привести к чрезмерному использованию памяти.

Вы предоставляете немного деталей о своем коде, поэтому я не могу быть более точным; в любом случае я бы попробовал и явный release объекты, если это возможно. Это избавило бы от двух проблем выше. Или вы можете попытаться обработать меньшие пулы автоматического выпуска (и вы бы исправили проблему второго типа)...

Я бы также предложил вам добавить монитор памяти в инструменты, чтобы вы увидели, насколько быстро система освобождает память.

PS: Теперь на картинке, которую вы прикрепили, меня поразило то, что диаграмма распределения абсолютно плоская: никаких распределений, никаких освобождений. Я ожидаю, что память будет увеличиваться и уменьшаться (по моему опыту, это признак здорового приложения для iOS).

Другие вопросы по тегам