Является ли завершение программы без освобождения всех динамически выделенных ресурсов рискованным?

Я знаю, что ресурсы, выделенные стеком, освобождаются в обратном порядке, поскольку они были выделены в конце функции как часть RAII. Я работаю над проектом и выделяю много памяти «новым» из библиотеки, которую использую, и тестирую материал. Я не добавил функцию выключения в качестве аналога функции инициализации, которая выполняет все динамическое распределение. Когда вы закрываете программу, я почти уверен, что утечки памяти нет, поскольку выделенная память должна быть освобождена операционной системой. По крайней мере, любая современная ОС, как объясняется в этом вопросе, похожем на мой: динамически выделяемая память после программы прекращение .

Мне интересно две вещи:

1: Существует ли в этом случае определенный порядок высвобождения ресурсов? Имеет ли это какое-то отношение к вашему написанному коду (т. е. к тому, в каком порядке вы его разместили) или это полностью зависит от ОС?

2: Причина, по которой я не сделал функцию отключения, чтобы отменить инициализацию, заключается в том, что я говорю себе, что сейчас просто тестирую материал, я сделаю это позже. Есть ли риск нанести какой-либо ущерб чему-либо, делая то, что я делаю? Худшее, что я могу себе представить, это то, что было упомянуто в ответе на тот вопрос, который я связал, а именно то, что ОС не может восстановить память, и вы получаете утечку памяти даже после выхода из программы.

Я следовал руководству по библиотеке физики Bullet и инициализировал такой код:

pSolver = new btSequentialImpulseConstraintSolver;
pOverlappingPairCache = new btDbvtBroadphase();
pCollisionConfig = new btDefaultCollisionConfiguration();
pDispatcher = new btCollisionDispatcher(pCollisionConfig);
pDynamicsWorld = new btDiscreteDynamicsWorld(pDispatcher, pOverlappingPairCache, pSolver, pCollisionConfig);

И никогда не вызывайте удаление для любого из них в данный момент, потому что, как я уже сказал, я просто тестирую.


person Zebrafish    schedule 12.08.2018    source источник
comment
Нет никакого риска, если вы не используете что-то, что, по словам ОС, сохраняется после процесса, например, общую память.   -  person Passer By    schedule 12.08.2018


Ответы (4)


Это зависит от ресурсов. Открытые файлы будут закрыты. Память будет освобождена. Деструкторы вызываться не будут. Созданные временные файлы не будут удалены.

Нет риска утечки памяти после выхода из программы.

person RalfFriedl    schedule 12.08.2018

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

На самом деле, если у вас много аллокаций, которые вы не удаляете до конца программы, может быть быстрее просто заставить ядро ​​очиститься после вас.

Однако деструкторы не запускаются. В основном это приводит к тому, что временные файлы не удаляются. Кроме того, это затрудняет отладку фактических утечек памяти.

Я предлагаю использовать std::unique_ptr<T> и не допускать утечки.

person Benno Straub    schedule 12.08.2018

Это зависит от того, как память распределяется фактически и от вашей хост-системы.

Если вы работаете только с классами, которые не переопределяют operator new(), И вы используете современную операционную систему, которая гарантирует освобождение ресурсов памяти при завершении процесса, тогда вся динамически выделенная память должна быть освобождена при завершении вашей программы. Порядок высвобождения памяти не гарантируется (например, объекты не будут высвобождаться в том же порядке или в порядке, обратном их построению). Единственный реальный риск в этом случае связан с ошибками в операционной системе хоста, которые приводят к неправильному управлению ресурсами программ/процессов (что является низким риском, но не нулевым риском для пользовательских программ в современных ОС Windows или Unix).

Если вы используете какие-либо классы, которые переопределяют operator new() (т. е. которые изменяют способ выделения необработанной памяти в процессе динамического построения объекта), тогда риск зависит от того, как на самом деле выделяется память, и каковы требования для освобождения. Например, если operator new() использует глобальные или общесистемные ресурсы (например, мьютексы, семафоры, память, разделяемую между процессами), то существует риск того, что ваша программа не освободит эти ресурсы должным образом, а затем косвенно вызовет проблемы для других программ, которые использовать одни и те же ресурсы. На практике, в зависимости от дизайна такого класса, необходимая очистка может быть в деструкторе, operator delete() или в какой-то их комбинации, но, как бы это ни было сделано, ваша программа должна будет явно освобождать такие объекты (например, delete выражение, которое соответствует выражению new), чтобы обеспечить правильное освобождение глобальных ресурсов.

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

Если ваша программа когда-либо будет собрана и запущена на хост-системе, не имеющей современной ОС, то нет никакой гарантии, что динамически выделенная память будет освобождена.

Если код в вашей программе когда-либо будет повторно использоваться в более крупной продолжительной программе (например, ваша функция main() переименовывается, а затем вызывается из другой программы в цикле), то ваш код может вызвать утечку памяти в этой более крупной программе.

person Peter    schedule 12.08.2018

Ничего страшного, так как операционная система (если только это не какая-то экзотическая или древняя ОС) не будет сливать память после завершения процесса. То же самое касается сокетов и дескрипторов файлов; они будут закрыты при выходе из процесса. Не убирать за собой нехорошо, но если вы этого не сделаете, это не повредит общей среде ОС.

Однако в вашем примере мне кажется, что единственная память, которую вам действительно нужно освободить, — это память pDynamicsWorld, поскольку остальные должны быть очищены экземпляром btDiscreteDynamicsWorld. Вы передаете их как аргументы конструктора, и я подозреваю, что они автоматически уничтожаются при уничтожении pDynamicsWorld. Вы должны прочитать документацию, чтобы убедиться.

Тем не менее, просто нехорошо (потому что это небезопасно) больше использовать delete. Таким образом, вместо использования delete для уничтожения pDynamicsWorld вы можете использовать вместо этого unique_ptr, который вы можете безопасно создать с помощью шаблона функции std::make_unique:

#include <memory>

// ...

// Allocate everything else with 'new' here, as usual.
// ...

// Except for this one, which doesn't seem to be passed to another
// constructor.      
auto pDynamicsWorld = std::make_unique<btDiscreteDynamicsWorld>(
    pDispatcher, pOverlappingPairCache, pSolver, pCollisionConfig);

Теперь pDispatcher, pOverlappingPairCache, pSolver и pCollisionConfig должны быть уничтожены pDynamicsWorld автоматически, а pDynamicsWorld будет автоматически уничтожено, когда выйдет за пределы области действия, потому что это unique_ptr.

Но опять же: прочитайте документацию Bullet Physics, чтобы проверить, действительно ли объекты, которые вы передаете в качестве аргументов конструкторам классов Bullet Physics, очищаются автоматически или нет.

person Nikos C.    schedule 12.08.2018