在不释放所有动态分配的资源的情况下结束程序是否有风险?

时间:2018-08-12 09:38:50

标签: c++ heap-memory dynamic-allocation

我知道堆栈分配的资源以相反的顺序释放,因为它们是作为RAII的一部分在函数末尾分配的。我一直在从事一个项目,我从正在使用的库中分配大量“ new”内存,并正在测试东西。我没有添加关闭函数作为执行所有动态分配的初始化函数的对应函数。当您关闭程序时,我非常确定没有内存泄漏,因为分配的内存应由操作系统回收。至少有任何现代OS,如在此问题中所述,类似于我的dynamically allocated memory after program termination

我想知道两件事:

1:在这种情况下,是否有特定的顺序释放资源?它与您编写的代码有什么关系(即,您分配它的顺序),还是完全取决于操作系统来完成它的工作?

2:之所以没有设置关机功能来反转初始化的原因是因为我对自己说我现在只是在测试东西,以后再做。做我做的事情是否有对任何东西造成任何损害的风险?我能想象到的最糟糕的是我链接的问题的答案中提到的内容,即操作系统无法回收内存,即使程序退出后,您仍然会发生内存泄漏。

我遵循了Bullet物理库教程,并初始化了一堆这样的代码:

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

现在绝对不要调用delete,因为正如我所说,我只是在测试。

4 个答案:

答案 0 :(得分:2)

这取决于资源。打开的文件将被关闭。内存将被释放。析构函数将不会被调用。创建的临时文件不会被删除。

程序退出后,没有内存泄漏的风险。

答案 1 :(得分:1)

由于程序可能会崩溃,因此有许多机制可以防止进程在停止后泄漏,并且泄漏通常不是很糟糕。

事实上,如果有很多分配要等到程序结束时才删除,那么在执行完后清理内核会更快。

但是析构函数不会运行。这主要是导致临时文件不会被删除。 同时,这也使得调试实际的内存泄漏更加困难。

我建议使用std::unique_ptr<T>,并且一开始不要泄漏。

答案 2 :(得分:1)

这取决于实际分配内存的方式以及您的主机系统。

如果仅使用不覆盖operator new()的类,并且使用的现代操作系统可确保在进程退出时释放内存资源,则应在程序执行时释放所有动态分配的内存终止。无法保证内存释放的顺序(例如,对象将不会以其构造的相同顺序或相反的顺序释放)。在这种情况下,唯一的真正风险与主机操作系统中的错误相关,该错误会导致程序/进程的资源得到不正确的管理(对于现代Windows或UNIX操作系统中的用户程序,这是低风险-但不是零风险)。

如果您正在使用任何覆盖operator new()的类(即,在动态构造对象的过程中更改了原始内存的分配方式),则风险取决于实际分配内存的方式以及对要求的要求用于释放。例如,如果operator new()使用全局或系统范围的资源(例如互斥体,信号量,进程之间共享的内存),则存在程序无法正确释放这些资源并间接导致问题的风险。对于使用相同资源的其他程序。实际上,根据此类的设计,所需的清除可能是在析构函数,operator delete()或二者的某种组合中进行的-但无论如何,您的程序将需要显式释放此类对象(例如,与delete表达式相对应的new表达式),以确保正确释放全局资源。

一个风险是动态调用对象的析构函数将不会被调用。如果您的程序依赖于析构函数执行除释放动态分配的内存(可能由类构造函数分配并由其他成员函数管理)之外的任何事情,那么将不执行其他清除操作。

如果您的程序将在没有现代OS的主机系统上构建并运行,则无法保证将回收动态分配的内存。

如果程序中的代码将在较大的长期运行的程序中重用(例如,您的main()函数被重命名,然后在循环中从另一个程序调用),则您的代码可能会导致该较大的程序发生内存泄漏。

答案 3 :(得分:1)

这很好,因为在该过程结束之后,操作系统(除非是某些异国情调的或古老的OS)不会泄漏内存。套接字和文件句柄也是如此;它们将在流程退出时关闭。不对自己进行清理不是一种好方法,但是如果不这样做,对整个操作系统环境没有任何危害。

但是,在您的示例中,在我看来,您实际上真正需要释放的唯一内存是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);

现在,pDispatcherpOverlappingPairCachepSolverpCollisionConfig应该被pDynamicsWorld自动销毁,pDynamicsWorld本身也会被销毁因为它是unique_ptr而超出范围时。

但是,再次:阅读Bullet Physics的文档,以检查作为参数传递给Bullet Physics类的构造函数的对象是否确实被自动清理了。