Отслеживайте все alloc / allocWithZone / dealloc в Objective-C

Извините за длинное описание, но вопросы не такие простые ...

Мой проект написан без GC. Недавно я обнаружил утечку памяти, которую не могу найти. Я безрезультатно использовал новый Xcode Analyzer. Я прочитал свой код построчно и проверил все alloc / release / copy / autorelease / mutableCopy / keep и пулы ... - по-прежнему ничего.

Преамбула: Стандартные инструменты и Omni Leak Checker по какой-то причине у меня не работают (Omin Tool отклоняет мое приложение, Instruments.app (Leaks) съедает слишком много памяти и ЦП, поэтому у меня нет возможности его использовать).

Итак, я хочу написать и использовать свой собственный код для перехвата и отслеживания «всей» статистики сообщений alloc / allocWithZone: / dealloc, чтобы написать некоторую простую собственную библиотеку проверки утечек (основная цель - только пометить имена классов объектов с возможными утечками).

Основная техника захвата, которую я использую:

  Method originalAllocWithZone = class_getClassMethod([NSObject class],@selector(allocWithZone:));
  if (originalAllocWithZone)
  {
   imp_azo = (t_impAZOriginal)method_getImplementation(originalAllocWithZone);
   if (imp_azo)
   {
    Method hookedAllocWithZone = class_getClassMethod([NSObject class],@selector(hookedAllocWithZone:));
    if (hookedAllocWithZone)
    {
     method_setImplementation(originalAllocWithZone,method_getImplementation(hookedAllocWithZone));
     fprintf(stderr,"Leaks Hook: allocWithZone: ; Installed\n");
    }
   }
  }
  • код, подобный этому, для перехвата метода alloc и dealloc как метод категории NSObject.

Я сохраняю IMP для реализации предыдущих методов, затем регистрирую и вычисляю все вызовы alloc / allocWithZone: как приращение (+1) значений статического массива NSInteger, а вызовы dealloc как декремент (-1).

В качестве конечной точки я называю предыдущую реализацию и возвращаемое значение.

По идее все работает нормально.

При необходимости я могу даже определить, является ли класс частью кластера классов (например, NSString, NSPathStore2; NSDate, __NSCFDate) ... с помощью некоторой функции нормализации (но это не имеет значения для проблем, описанных ниже).

Однако у этого метода есть некоторые проблемы:

  • Не все классы могут быть пойманы, например, [NSDate date] не улавливается в alloc / allocWithZone: вообще, однако я вижу вызов alloc в GDB
  • Поскольку я пытаюсь использовать технику автоматического обнаружения одиночных объектов (на основе keepCount readind) для автоматического исключения некоторых объектов из окончательной статистики, создание NSLocale зависает на этапе предварительной инициализации при запуске полного приложения Cocoa (на самом деле, даже простой командной строки Objective-C утилита с включенным фреймворком Foundation имеет некоторую дополнительную инициализацию перед main ()) - GDB использует allocWithZone: вызовы один за другим, ....

Полные исходники проекта концептуального проекта загружены здесь: http://unclemif.com/external/DILeak.zip (3.5 Кб)

Запустите make из Terminal.app, чтобы скомпилировать его, запустите ./concept, чтобы показать его в действии.


1-й вопрос: почему я не могу уловить все распределения объектов, подключив методы alloc и allocWithZone:?

2-й вопрос: зачем подключили allocWithZone: зависает в CFGetRetainCount (или [inst keepCount]) для некоторых классов ...


person UncleMiF    schedule 28.12.2009    source источник
comment
Ух ты. Это действительно далеко, чтобы избежать использования очевидных решений, потому что они слишком медленные или используют слишком много памяти, и все же, интересно, любой такой аргумент вообще действителен, когда инструменты (какие бы вы ни использовали) нельзя оставлять в окончательной сборке, но будет использоваться только при подозрении на проблему. Я думаю, что для 99,999% людей использование стандартных инструментов - правильный ответ, а использование собственных инструментов - странное решение, которое немногие другие разработчики сочтут полезным.   -  person Warren P    schedule 01.04.2010


Ответы (3)


Даже без прибора Leaks Instruments все равно могут вам помочь.

Начните с шаблона Leaks, затем удалите из него инструмент Leaks (поскольку вы говорите, что он использует слишком много памяти). Только ObjectAlloc сообщит вам все распределения и освобождения ваших объектов, а также (с включенной опцией, которая есть по умолчанию в шаблоне Leaks) все их удержания и освобождения.

Вы можете настроить инструмент ObjectAlloc так, чтобы он отображал только те объекты, которые еще существуют; если вы доведете приложение до точки, в которой не должно существовать никаких объектов (или объектов определенного класса), а такие объекты все еще существуют, то у вас есть утечка. Затем вы можете развернуть, чтобы найти причину утечки.

Это видео может помочь.

person Peter Hosey    schedule 28.12.2009
comment
Спасибо. Этот способ предпочтительнее, но, как я уже говорил, иногда инструмент Instruments съедает слишком много памяти, и отслеживаемое приложение нереально тормозит ... Но, ограничивая некоторые входные данные и время мониторинга, возможно сотрудничество с инструментами. - person UncleMiF; 28.12.2009
comment
Теперь я использую следующее решение для обнаружения утечек, потребляющих память: Запустить инструменты, открыть шаблон утечек, удалить дорожку выделения объектов, запустить приложение только с утечками (также я установил время автоматического обнаружения на 30-60 секунд). Это дает 10-кратную экономию памяти и времени при отслеживании утечек! - person UncleMiF; 28.12.2009
comment
Классное решение Питер, эта штука с инструментами удивительно гибкая. - person Warren P; 01.04.2010

Начните с шаблонов Xcode. Не пытайтесь свернуть свою собственную процедуру main () для приложения какао, пока не поймете, что делаете.

person NSResponder    schedule 28.12.2009
comment
Есть много причин использовать собственный main (). Но это не один из них. - person bbum; 28.12.2009
comment
Да, пример Demo - это не Cocoa - это просто утилита командной строки, которая использует платформу Foundation для упрощения демонстрации. Разница между способом Какао и этим простым приложением заключается в скрытой инициализации перед main () / NSApplicationMain (). Попробуйте раскомментировать строку NSLocale в файле concept.m - чтобы увидеть проблему зависания, которую вы увидите, если вы создадите библиотеку DILeak с исходным приложением Cocoa. Посмотрите на [NSDate date], чтобы убедиться, что он не был зарегистрирован системой отслеживания размещения. - person UncleMiF; 28.12.2009

person    schedule
comment
Он сказал, что пишет это без GC. Однако информативный ответ для тех из нас, кто пишет WITH GC. - person jbrennan; 28.12.2009
comment
Хорошие советы. Спасибо. Я попробовал инструмент утечки. Однако моя фоновая цель - написать некоторую библиотеку для автоматической проверки утечек из кода в сборке DEBUG для модульных тестов. Итак, мне нужны контрольные контрольные точки из кода - активируйте обнаружение утечек для некоторых потоков, прочтите статистику ... чтобы автоматизировать процедуру ... Я нашел свои утечки. Ничего загадочного - просто пропустил автоспуск в одном месте. Однако оба моих вопроса все еще открыты. - person UncleMiF; 28.12.2009