Я работаю над большим проектом C ++. Он состоит из сервера, который предоставляет REST API, предоставляя простой и удобный интерфейс для очень широкой системы, включающей множество других серверов. Кодовая база довольно большая и сложная, и развивалась во времени без надлежащего предварительного проектирования. Моя задача - реализовать новые функции и реорганизовать / исправить старый код, чтобы сделать его более стабильным и надежным.
На данный момент сервер создает ряд долгоживущих объектов, которые никогда не завершаются и не удаляются при завершении процесса. Это делает Valgrind практически непригодным для обнаружения утечек, поскольку невозможно отличить тысячи (сомнительно) законных утечек от «опасных».
Моя идея состоит в том, чтобы обеспечить удаление всех объектов до завершения, но когда я сделал это предложение, мои коллеги и мой начальник выступили против того, чтобы я указал, что ОС все равно освободит эту память (что очевидно для всех), и избавился от объектов замедлит выключение сервера (что на данный момент является в основном вызовом std::exit
). Я ответил, что «чистая» процедура выключения не обязательно означает, что ее нужно использовать. Мы всегда можем позвонить std::quick_exit
или просто kill -9
процесс, если мы чувствуем нетерпение.
Они ответили: «Большинство демонов и процессов Linux не беспокоятся об освобождении памяти при завершении работы». Хотя я вижу это, верно и то, что нашему проекту действительно нужна точная отладка памяти, поскольку я уже обнаружил повреждение памяти, двойное освобождение и неинициализированные переменные.
Что ты думаешь? Я преследую бессмысленные усилия? Если нет, то как я могу убедить моих коллег и моего начальника? Если так, почему и что я должен делать вместо этого?
Ответы:
Добавьте переключатель к процессу сервера, который можно использовать во время измерений valgrind, который освободит всю память. Вы можете использовать этот переключатель для тестирования. Воздействие будет минимальным во время нормальной работы.
У нас был длительный процесс, который занимал несколько минут, чтобы выпустить тысячи объектов. Гораздо эффективнее было просто выйти и позволить им умереть. К сожалению, как вы указали, это затруднило обнаружение истинных утечек памяти с помощью valgrind или любых других инструментов.
Это был хороший компромисс для нашего тестирования, не влияющий на нормальную производительность.
источник
Ключ здесь заключается в следующем:
Это в значительной степени прямо подразумевает, что ваша кодовая база собрана из ничего, кроме надежды и строки. Компетентные программисты на C ++ не имеют двойного освобождения.
Вы абсолютно преследуете бессмысленные усилия, как, например, вы решаете один крошечный признак реальной проблемы, а именно то, что ваш код примерно так же надежен, как и служебный модуль Apollo 13.
Если вы правильно запрограммируете свой сервер с помощью RAII, эти проблемы не возникнут, и проблема в вашем вопросе будет устранена. Кроме того, ваш код может действительно время от времени выполняться правильно. Таким образом, это явно лучший вариант.
источник
Один хороший подход - сузить дискуссию с вашими коллегами с помощью классификации. Учитывая большую кодовую базу, безусловно, есть не одна, а несколько (идентифицируемых) причин для долгоживущих объектов.
Примеры:
Долгоживущие объекты, на которые никто не ссылается (настоящие утечки). Это ошибка логики программирования. Исправьте те, которые имеют более низкий приоритет, если они не ответственны за то, что ваш объем памяти будет расти со временем (и ухудшать качество ваших приложений). Если они увеличивают объем памяти, исправьте их с более высоким приоритетом.
Долгоживущие объекты, на которые все еще ссылаются, но они больше не используются (из-за логики программы), но которые не увеличивают объем вашей памяти. Просмотрите код и попробуйте найти другие ошибки, которые приводят к этому. Добавьте комментарии к базе кода, если это преднамеренная (производительность) оптимизация.
Долгоживущие объекты «по замыслу». Синглтон шаблон например. От них действительно трудно избавиться, особенно если это многопоточное приложение.
Переработанные объекты. Долгоживущие объекты не всегда должны быть плохими. Они также могут быть полезными. Вместо выделения / освобождения высокочастотной памяти добавление неиспользуемых в настоящее время объектов в контейнер для извлечения, когда такой блок памяти снова необходим, может помочь ускорить работу приложения и избежать фрагментации кучи. Их должно быть легко освободить во время выключения, возможно, в специальной сборке «КИПиА / проверено».
«разделяемые объекты» - объекты, которые используются (на которые ссылаются) множеством других объектов, и никто точно не знает, когда это будет сохранено для их освобождения. Рассмотрите возможность превращения их в объекты с подсчетом ссылок.
После того, как вы классифицировали истинные причины этих несвободных объектов, гораздо проще ввести индивидуальное обсуждение и найти решение.
источник
ИМХО, время жизни этих объектов никогда не должно быть просто сделано и оставлено умирать, когда система выключается. Это пахнет глобальными переменными, которые мы все знаем, плохо плохо плохо плохо плохо. Особенно в эпоху умных указателей нет никаких причин делать это, кроме лени. Но что еще более важно, это добавляет уровень технической задолженности вашей системе, с которой кто-то может столкнуться когда-нибудь.
Идея «технического долга» заключается в том, что когда вы используете такой ярлык, когда кто-то хочет изменить код в будущем (скажем, я хочу, чтобы клиент мог перейти в «автономный режим» или «спящий режим»). ", или я хочу иметь возможность переключать серверы без перезапуска процесса) им придется приложить усилия, чтобы сделать то, что вы пропускаете. Но они будут поддерживать ваш код, поэтому они не будут знать о нем почти столько же, сколько вы, поэтому это займет у них намного больше времени (я не говорю на 20% дольше, я говорю в 20 раз дольше!). Даже если это вы, вы не будете работать над этим конкретным кодом в течение нескольких недель или месяцев, и вам понадобится гораздо больше времени, чтобы стряхнуть паутину, чтобы правильно ее реализовать.
В этом случае оказывается, что у вас очень тесная связь между объектом сервера и «долгоживущими» объектами ... бывают случаи, когда запись может (и должна) пережить соединение с сервером. Сохранение каждого отдельного изменения объекта на сервере может быть чрезмерно дорогим, поэтому обычно лучше, чтобы порожденные объекты действительно были привязаны к объекту сервера, с вызовами сохранения и обновления, которые фактически изменяют сервер. Обычно это называется шаблоном активной записи. Видеть:
http://en.wikipedia.org/wiki/Active_record_pattern
В C ++ у меня была бы каждая активная запись, имеющая слабый_птр к серверу, который мог бы интеллектуально генерировать вещи, если соединение с сервером становится темным. Эти классы могут заполняться либо лениво, либо периодически, в зависимости от ваших потребностей, но время жизни этих объектов должно быть только там, где они используются.
Смотрите также:
Это пустая трата времени, чтобы освободить ресурсы, прежде чем я выйду из процесса?
Другой
источник
This reeks of global variables
Как вы переходите от «есть тысячи объектов, которые нужно освободить» к «они должны быть глобальными»? Это довольно скачок логики.Если вы можете легко определить, где расположены объекты, которые должны выживать в течение неопределенного времени, однажды можно будет выделить их, используя альтернативный механизм распределения, чтобы они либо не отображались в отчете об утечке valgrind, либо просто представлялись как единое распределение.
Если вы не знакомы с этой идеей, вот статья о том, как бесполезно распределять память в C ++ , хотя обратите внимание, что ваше решение может быть проще, чем примеры из этой статьи, поскольку вам вообще не нужно обрабатывать удаление !
источник