Мой страх перед утечками памяти оправдан?
Короткий ответ - да. Длинный ответ - да, но есть методы их уменьшения и, как правило, упрощения. Самым важным из этих вещей, по моему мнению, не является легкое использование new / delete и разработка вашей программы для уменьшения или уменьшения как можно большего динамического распределения. Вместо того, чтобы выделять память, попробуйте следующее и выделяйте свою собственную память только тогда, когда ни один из этих методов не работает для вас (примерно в порядке, который вы предпочитаете):
- Распределить в стеке
- Используйте стандартные контейнеры (C ++ Standard Library и / или Boost), например, вместо написания собственного связанного списка, используйте std :: list
- В связи с вышеизложенным сохраняйте объекты по значению, если можете (то есть, если создание копии не слишком дорого), или, по крайней мере, ссылки (со ссылками, которые вам не нужно беспокоиться о нулевом значении) для размещения выделенных объектов
- Передача ссылок на объекты стека, где это возможно
- Когда вам нужно выделить свою собственную память, попробуйте использовать RAII для выделения в конструкторе и снова освободить его в деструкторе. Убедитесь, что этот объект расположен в стеке.
- Когда вам нужно использовать указатели для распределения данных вручную, используйте умные указатели для автоматического освобождения объектов, которые больше не используются
- Четко определите, каким объектам принадлежит какая память. Постарайтесь ограничить вашу иерархию классов как можно меньшим количеством владельцев ресурсов и позволить им заниматься распределением и освобождением объектов для вас
- Если вам нужен более общий доступ к динамической памяти, напишите менеджер ресурсов, который станет владельцем объектов. Система дескрипторов , как описано здесь , также полезна, поскольку она позволяет вам «собирать мусор» в памяти, которая больше не нужна. Дескрипторы также могут быть помечены тем, какой подсистеме принадлежит какая память, поэтому вы можете вывести состояние использования системной памяти, например, «подсистеме А принадлежит 32% выделенной памяти»
- Перегрузка операторов new и delete для выделенных классов, чтобы вы могли поддерживать дополнительные метаданные о том, кто / что / где / когда выделил, что
Как узнать, где память
Утечка лжи?
Valgrind набор инструментов : Memcheck, Cachegrind, Callgrind, Massif, Helgrind ...
Вы также можете попробовать скомпилировать с electric fence (-lefence for gcc) или вашими компиляторами. Вы также можете попробовать набор инструментов Intel, особенно если вы пишете многопоточный код или чувствительный к производительности код (например, Parallel Studio), хотя они и дороги.
Нет хороших инструментов, которые помогают в
найти источник утечек памяти
сегодня
Конечно, есть. Смотри выше.
Теперь, так как вы пишете игру, я поделюсь некоторыми своими мыслями / опытом по разработке игр:
- Предварительно выделите как можно больше (в идеале все) в начале каждого уровня. Тогда во время уровня вам не нужно беспокоиться об утечках памяти. Сохраняйте указатели на все, что вы выделили, и в начале следующего уровня освобождайте их все, так как не должно быть возможности существования висячих указателей, когда следующий уровень загружает свой собственный чистый набор данных.
- Используйте распределители стека (либо используя фактический стек, либо создавая свой собственный в куче), чтобы управлять распределениями внутри уровня или фрейма. Когда вам нужна память, вытолкните ее из верхней части стека. Затем, когда этот уровень или кадр будет завершен, вы можете просто очистить стек (если вы храните только типы POD, это быстро и просто: просто сбросьте указатель стека. Я использую это для размещения сообщений или системы обмена сообщениями в моем собственном игровом движке : во время кадра сообщения (типы POD фиксированного размера в моем движке) распределяются из стекового пула памяти (чья память предварительно выделена). Все события просто берутся из стека. В конце кадра я «поменять» стек на второй (чтобы обработчики событий могли также отправлять события) и вызвать каждый обработчик событий. Наконец, я просто сбрасываю указатель. Это делает распределение сообщений очень быстрым и невозможным утечку памяти.
- Используйте систему ресурсов (с дескрипторами) для всех ресурсов, которыми вы не можете управлять через пулы памяти или распределители стека: буферы, данные уровня, изображения, аудио. Моя система ресурсов, например, также поддерживает потоковые ресурсы в фоновом режиме. Дескрипторы создаются немедленно, но помечаются как «не готовы», пока ресурс не завершил потоковую передачу.
- Создайте ваши данные, а не код (то есть сначала спроектируйте структуры данных). По возможности изолируйте выделение памяти.
- Попробуйте сохранить похожие данные вместе . Это не только облегчит управление его временем жизни, но также может улучшить производительность ваших движков за счет лучшего использования кэша (например, все позиции символов хранятся в контейнере позиций, все позиции обновляются / обрабатываются вместе и т. Д.).
- Наконец, если вы можете программировать в максимально возможном чисто функциональном стиле, вместо того, чтобы слишком полагаться на ООП, вы можете упростить ряд проблем: управление памятью проще, потому что то, какие части вашего кода могут изменять данные, ограничено. Распределение происходит перед вызовом функций, освобождение - когда они завершены (конвейер потоков вызовов функций). Во-вторых, если вы имеете дело с чисто функциональным кодом, работающим с неизменяемыми данными, многоядерное программирование будет значительно упрощено. Двойная победа. Например, в моем движке данные игровых объектов обрабатываются чисто функциональным кодом, который принимает в качестве входных данных текущее состояние игры и возвращает в качестве выходных данных следующее игровое состояние кадров. Это позволяет очень легко отследить, какие части кода могут выделить или освободить память, и, как правило, отследить время жизни объектов. Из-за этого я могу параллельно обрабатывать игровые объекты.
Надеюсь, это помогло.