Есть ли какие-либо подсказки/приемы для нахождения циклических ссылок shared_ptr?
Это - экс-клен того, что я пытаюсь найти - к сожалению, я, может казаться, не нахожу цикл в своем коде.
struct A
{
boost::shared_ptr<C> anC;
};
struct B
{
boost::shared_ptr<A> anA;
};
struct C
{
boost::shared_ptr<B> anB;
};
Я использовал комбинацию вышеуказанных сообщений. Я использовал профилировщик памяти, придумал некоторые подозрительные циклы и сломал их, используя weak_ptr.
Я и раньше использовал встроенное обнаружение утечки памяти CRT, но, к сожалению, в моем случае есть несколько статических синглтонов, которые не освобождаются до выгрузки модуля, что, как я полагаю, происходит после жизненного цикла детекторов CRT. В основном это дает много ложных срабатываний.
Однажды я отвечал за разработку системы кредитного риска (на языке С++, хотя это не относится к делу). Это действительно большие графики с распределением риска по узлам. У нас была простая эвристика, чтобы выяснить, были ли мы в цикле - если мы проехали более 500 раз (я забыл точную цифру - она была настраиваемой), то ответ был "да". Большинство схем обнаружения циклов полагаются на эвристику вроде этой.
В прошлом у меня были похожие проблемы - утечки памяти из-за циклических ссылок shared_ptr, которые оставались незамеченными в течение нескольких месяцев.
Следите за "кэшами". У меня есть объект (назовем его "Factory"), который обрабатывал элементы ("Виджет"). Виджеты имели свойство быть A) Неизменяемыми, а B) Имели shared_ptr
к своему создателю (он иногда создавал другие виджеты и т.д.). Все работало отлично, пока я не добавил кэш виджетов на Factory - так как виджеты были неизменяемыми, имело смысл кэшировать их, возвращать один и тот же виджет обратно каждый раз, когда он запрашивался. Мой кэш был кэшем shared_ptr
, так что мгновенная бесшумная утечка. Исправления очевидны, поэтому я не буду вдаваться в них.
В конце концов, я смог опереться на платформу, которую использовал для обнаружения таких утечек памяти из CRT. В CRT Visual Studio есть обнаружение утечек памяти и составление отчетов, которые я включил в свою тестовую программу для предотвращения регрессий:
int main()
{
// code code code
// code code code
#ifdef _MSC_VER
_CrtSetReportMode( _CRT_WARN, _CRTDBG_MODE_FILE );
_CrtSetReportFile( _CRT_WARN, _CRTDBG_FILE_STDOUT );
_CrtSetReportMode( _CRT_ERROR, _CRTDBG_MODE_FILE );
_CrtSetReportFile( _CRT_ERROR, _CRTDBG_FILE_STDOUT );
_CrtSetReportMode( _CRT_ASSERT, _CRTDBG_MODE_FILE );
_CrtSetReportFile( _CRT_ASSERT, _CRTDBG_FILE_STDOUT );
_CrtDumpMemoryLeaks();
#endif
}
GCC, вероятно, имеет похожие базовые отчеты об утечках, но я не знаю, что это такое.
.Можно реализовать некий отладочный интерфейс, возвращающий список share_ptrs, принадлежащих этому объекту. Это нужно будет сделать для каждого класса, хранящегося в shared_ptr. Теперь у вас есть общий граф, который можно обойти, и на нем можно использовать алгоритмы определения циклов. Думаю Алгоритм сильно связанных компонент Тарьяна мог бы подойти для этого, но теория графов - это не мое состояние.
.Я бы порекомендовал использовать Valgrind . Когда вы закроете процесс, он покажет вам всю утечку памяти. Если выключение как-то не прерывает цикл, то любые циклы должны отображаться как утечка памяти, и Valgrind скажет вам, откуда в вашем коде изначально была выделена память.
Я думаю, что самый простой ответ заключается в том, что есть только столько умных указателей, которые могут сделать для вас:
Я предлагаю записывать всякий раз, когда вы делаете цикл, (легко, если вы создаете все три объекта одновременно, в противном случае ...), а затем проверять ту запись, где вы удаляете / развязываете объекты, или просто периодически, если это не возможно.