Освобожденные объекты освобождены, все еще не хватает памяти?
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).