Я бил головой о стену, пытающуюся выяснять, как у меня была утечка памяти в собравшем "мусор" приложении Какао. (Использование памяти в Мониторе Действия просто выросло бы и выросло бы, и выполнение приложения с помощью инструментов Монитора GC также покажет постоянно растущий график.)
Я в конечном счете сузил его к единственному шаблону в моем коде. Данные загружались в NSData и затем анализировались библиотекой C (байты данных, и длина были переданы в него). Библиотека C имеет обратные вызовы, которые запустили бы и возвратили бы подстроку стартовые указатели и длины (для предотвращения внутреннего копирования). Однако в моих целях, я должен был превратить их в NSStrings и иметь в наличии их некоторое время. Я сделал это при помощи initWithBytes:length:encoding NSSTRING: метод. Я предположил, что это скопирует байты, и NSString управлял бы им соответственно, но что-то идет не так, как надо, потому что это протекает как сумасшедший.
Этот код "пропустит" или так или иначе обманет сборщик "мусора":
- (void)meh
{
NSData *data = [NSData dataWithContentsOfFile:[[NSBundle mainBundle] pathForResource:@"holmes" ofType:@"txt"]];
const int substrLength = 80;
for (const char *substr = [data bytes]; substr-(const char *)[data bytes] < [data length]; substr += substrLength) {
NSString *cocoaString = [[NSString alloc] initWithBytes:substr length:substrLength encoding:NSUTF8StringEncoding];
[cocoaString length];
}
}
Я могу поместить это в таймер и просто наблюдать, что использование памяти повышается и с Монитором Действия, а также с инструментом Монитора GC. (holmes.txt составляет 594 КБ),
Это не лучший код в мире, но он показывает проблему. (Я работаю 10.6, проект предназначен для 10,5 - если это будет иметь значение). Я перечитал по документам сборки "мусора" и заметил много возможных ловушек, но я не думаю, что делаю что-либо, очевидно, против правил здесь. Не повреждает спрашивать, все же.Спасибо!
Вот рис. графа объектов, просто растущего и растущего:
Это несчастный край. Пожалуйста, подайте ошибку ( http://bugreport.apple.com/ ) и прикрепите свой отличный минимальный пример.
Проблема в двух разах;
Основной контур событий не работает и, таким образом, коллектор не запускается через активность MEL. Это оставляет коллекционер, выполняющий свой обычный фон только на основе пороговых коллекций.
Данные хранят данные, прочитанные из файла в буфер Malloc'D, который выделяется из зоны Malloc. Таким образом, у GC приходится распределение - сам объект NSDATA - действительно крошечный, но указывает на что-то действительно большое (распределение Malloc). Конечный результат заключается в том, что порог коллектора не ударяется, и он не собирается. Очевидно, что улучшение этого поведения желательно, но это сложная проблема.
Это очень простая ошибка для воспроизведения в микро-ориентировании или в изоляции. На практике, как правило, происходит достаточно, чтобы эта проблема не произойдет. Однако могут быть определенные случаи, когда он становится проблематичным.
Измените свой код к этому, и сборщик собирает объекты данных. Обратите внимание, что вы не должны использовать Collectexhaustlive
часто - он ест CPU.
- (void)meh
{
NSData *data = [NSData dataWithContentsOfFile:[[NSBundle mainBundle] pathForResource:@"holmes" ofType:@"txt"]];
const int substrLength = 80;
for (const char *substr = [data bytes]; substr-(const char *)[data bytes] < [data length]; substr += substrLength) {
NSString *cocoaString = [[NSString alloc] initWithBytes:substr length:substrLength encoding:NSUTF8StringEncoding];
[cocoaString length];
}
[data self];
[[NSGarbageCollector defaultCollector] collectExhaustively];
}
[Self] [данные]
сохраняет объект данных в живых после последней ссылки на него.