Утечка памяти с сборкой мусора Какао - PullRequest
5 голосов
/ 19 января 2010

Я бился головой о стену, пытаясь выяснить, как у меня произошла утечка памяти в приложении Cocoa со сборкой мусора. (Использование памяти в Activity Monitor будет только расти и расти, а запуск приложения с использованием инструментов GC Monitor также будет показывать постоянно растущий график.)

Я в конечном итоге сузил его до одного шаблона в моем коде. Данные загружались в NSData, а затем анализировались библиотекой C (в них передавались байты и длина данных). Библиотека C имеет обратные вызовы, которые запускают и возвращают начальные указатели и длины подстроки (чтобы избежать внутреннего копирования). Тем не менее, для моих целей мне нужно было превратить их в строки NSS и держать их некоторое время. Я сделал это с помощью initWithBytes: длина: кодирование: метод 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];
    }
}

Я могу поставить это в таймер и просто наблюдать за увеличением использования памяти с помощью Activity Monitor, а также с помощью инструмента GC Monitor. (holmes.txt - 594 КБ)

Это не лучший код в мире, но он показывает проблему. (У меня работает 10,6, проект рассчитан на 10,5 - если это имеет значение). Я перечитал документы по сбору мусора и заметил ряд возможных подводных камней, но я не думаю, что я делаю что-то явно противоречащее правилам. Не больно спрашивать, хотя. Спасибо!

Почтовый индекс проекта

Вот изображение графа объектов, который только растет и растет:

alt text

1 Ответ

13 голосов
/ 19 января 2010

Это неудачный крайний случай. Пожалуйста, отправьте сообщение об ошибке (http://bugreport.apple.com/) и приложите свой превосходный минимальный пример.

Проблема в два раза;

  • Основной цикл событий не запущен, и, следовательно, сборщик не запускается из-за активности MEL. Это оставляет коллектор, делающий свои обычные фоновые только пороговые коллекции.

  • Данные сохраняют данные, прочитанные из файла, в буфер malloc, который выделяется из зоны malloc. Таким образом, распределение с учетом GC - сам объект NSData - действительно крошечный, но указывает на что-то действительно большое (распределение malloc). Конечным результатом является то, что порог коллектора не достигнут, и он не собирает. Очевидно, что улучшение этого поведения желательно, но это сложная проблема.

Это очень простая ошибка, которую можно воспроизвести в микро-бенчмарке или отдельно. На практике, как правило, достаточно, чтобы эта проблема не возникала. Однако могут быть определенные случаи, когда это становится проблематичным.

Измените свой код на этот, и сборщик будет собирать объекты данных. Обратите внимание, что вы не должны использовать collectExhaustively часто - он потребляет процессор.

- (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];
}

[data self] сохраняет объект данных живым после последней ссылки на него.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...