本人是新手,如果说得不对还请多多包涵和指点。
最近心血来潮想要写个项目,期间遇到了这样一个问题:
我需要在异常访问的时候强制中断程序,那么,使用exit()到底会不会导致内存泄漏?
内存泄漏是什么,这个问题我想不必多说,何况我也不是什么权威,说错了怕贻笑大方,在此省略;我本来是想在网上寻找答案,但是找遍了网上,也不见得能给出一个较为妥当的答案(为此我甚至去了cplusplus.com,可能是我英语不过关,硬是找不到好的答案);一怒之下,我只好自己写代码来测试(以身试险),闲话不多说,让我们看看我测试用的代码:
#include <cstdlib>
int main(int argc, char* argv[])
{
long long *a = new long long;
for (unsigned i = 0; i < 53687091; i++)
a = new long long;
system("pause");
exit(1);
return 0;
}
关于测试代码,在这里我要做出几点概述:
1.之所以没有iostream或者cstdio,是因为根本不需要输入输出什么东西;
2.如果直接使用long long a[],可能会触发C++实现的某些我不了解的内存回收机制,导致异常退出时强制回收空间;还可能会发生一个情况,因为typename varname[]是向系统申请一段连续的空间,如果内存中碎片很多的情况下,可能直接就申请空间失败;
3.对于配置不好的计算机,请适度调整循环次数,否则可能导致无法预料的情况发生(如死机、崩溃);
4.申请这么多次,需要0.5s以上的时间很正常,请耐心等待;
5.我是Linux环境入门的C,习惯了int main(int argc, char* argv[])的写法,大家可以改成自己想要的(但在我的编译环境下是不予通过void main()的)
0.请不要在任何项目中模仿该代码;
OK,代码准备完毕,测试开始:
开始前:
我的电脑配置也是一般般,总内存4GB,可用内存大概2.37GB
循环完成后:
虽然是意想之中的,但是还是被吓了一跳呢。
此时程序并没有退出,需要我们摁下任意键,然后执行exit()强制退出;
于是我摁下了回车键,任务管理器的答复是:
完美!
如果Windows能办到,相信Linux不会比Windows差的,甚至更好也说不定;
这说明了一个规范的操作系统有良好的内存回收机制,对于一个进程申请的所有内存页面都有一个标记,当进程被结束掉的时候,系统会自动释放掉该进程申请的内存页面,无论是被跟踪的内存还是已经丢失的内存(相对于该进程而言)。
我想,系统的反射机制不可能如此脆弱,能被常见的问题给摧毁。一个进程如果不被中止(如后台进程之类),可能会一直持续产生内存泄漏和内存碎片,但是一旦该进程被终止,其申请的内存页面也将关闭。
但是,我们设计的大部分项目,都有一定的运行时间,如果造成众多的内存泄漏,必然会导致不可预料的事情发生;最好的方法是,跟踪每一个申请的内存页面,在恰当的时候将它结束掉,说到这个,我就想起了以前写的一段代码,映像深刻啊:(这个是我在学线性代数的时候写的,当时是为了验证我做的矩阵相乘作业的正确性)
PS:为了节约篇幅,我将不重要的代码全部用"..."代替;
#include <iostream>
template <class TYP_FR_MTRX>
class matrix
{
public:
...
//重头戏来了
matrix<TYP_FR_MTRX>& operator * (const matrix<TYP_FR_MTRX>& mltplr) const
{
matrix<TYP_FR_MTRX>* answer = new matrix<TYP_FR_MTRX>(this->height, mltplr.length, nullptr);
...
return *answer;
}
private:
unsigned height;
unsigned length;
TYP_FR_MTRX* detail;
};
说明三点:
1.如果矩阵中只需用到整数,我就可以要matrix<int>来满足需求,我之所以写成模板类而不是一个具体的类,目的就是可以随意使用浮点型和整型(同时能锻炼模板类的书写格式)
2.貌似模板这个东西一直是C++的心头大患,如果把模板类中的函数写到别的文件,貌似在很多编译器下连接出错,所以我直接写在一起了;
3.因为是给自己用,所以没必要判断什么到底能不能相乘啊之类的;
矩阵乘法中,返回值也是matrix类的一个实例,那么它就必定带有:两个unsigned int大小的空间,一个指针型大小的空间,和指针访问的一片空间;如果此时,我们用一个matrix<type-name>来接收它的返回值,那么析构的时候,只会析构掉其自己的两个unsigned int大小的空间,一个指针型大小的空间,及其指针访问的一片空间(也就是我们new出来的那个)。
其中,answer所new出来的两个unsigned int大小的空间,一个指针型大小的空间没有被析构掉,这很微小,但是却不容忽视。
我当时使用的方法是,matrix<type-name>* p = &(a * b);其中a与b是与*p同type-name的两个实例;这样,我们delete p,可以发现在operator*()中构造的实例被完整的析构了;(测试方法:在构造函数里打印一行"构造",在析构函数里打印一行"析构")
现在想想,当时还真的有点蠢,实际上我们可以通过重载'='号来完美完成这个内存回收机制:
matrix<TYP_FR_MTRX>& operator = (matrix<TYP_FR_MTRX> const& rghnd)
{
const unsigned sz_mtrx = rghnd.length * rghnd.height;
if (this->detail != nullptr)
delete[] this->detail;
if (rghnd.length != 0 && rghnd.height != 0 && rghnd.detail != nullptr)
{
this->height = rghnd.height;
this->length = rghnd.length;
this->detail = new TYP_FR_MTRX[sz_mtrx];
for (register unsigned i = 0u; i < sz_mtrx; i++)
this->detail[i] = rghnd.detail[i];
}
else
{
this->height = this->length = 0;
this->detail = nullptr;
}
return *this;
}
这样,当用函数返回值的时候,就不用担心其局部实例被析构掉,让我们指向一片非法内存了,而且这也避免了两个matrix<>指向同一片空间,一举两得。
在写项目的时候,就可以类似的这么去用,尽量确保每一片申请的内存空间都能被访问,即使因为各种原因无法完全确保,也需要我们将内存泄漏和内存碎片问题降到最低。至于原因,我想大家都知道,没有哪个用户喜欢一点开就死机的程序。
注:
1.本文原创,若转载请注明原作者和出处
2.实验环境:Windows 7旗舰版(正版)、G++编译器(GNUC++Compiler使用Code::Blocks作为IDE环境)
3.对malloc()也同样进行了测试,请C语言程序员放心