为什么我的软件内存占用这么高?从内存占用过高到C++内存管理方法
背景
在尝试写一个PDF文件标注软件时,需要将PDF文件转化为图片然后再展示给用户,同时允许用户在图片上做标注。
实现概况
假设用下面3各类:PDFFile
、PDFPage
、Mark
分别对应PDF文件,文件中的每个页面以及页面中的标注信息:
class Mark {Mark(QRect _pos);~Mark() = default;void paint();// ...
private:QRect pos;// ...
}class PDFPage {
public:PDFPage(QImage *_img);~PDFPage() = default;void paint();void addMark(Mark *mark);// ...
private:QImage *img;std::vector<Mark*> marks;// ...
}class PDFFile {
public:PDFFile(QString file_path);~PDFFile() = default;// ...
private:std::vector<PDFPage> pages;// ...
}
每次打开新的PDF文件,就会将保存PDF文件的PDFFile
指针指向新的文件。
问题
上述实现貌似没什么问题,但当多次打开PDF文件时,内存占用会快速增加,每打开一个新的PDF文件,都会增加一部分内存占用,直至软件被操作系统kill。
假设为了PDF页面展示的清晰度,转化时做了放大,导致QImage占用的内存比较大。那么内存占用一直增大的原因是什么呢?
C++中常见的内存管理方法
上面代码的问题处在PDFPage
的析构函数上,PDFPage
用指针指向QImage,但在析构函数中并没有主动释放图片占用的大块内存,这就导致了内存泄漏,也就是程序内存一直增加直至crash。
修改PDFPage
的析构函数如下,就能解决内存泄露的问题:
~PDFPage() {delete img;for (auto mark : marks) {delete mark;}
}
虽然上述析构函数能够解决内存泄漏的问题,但如果实现比较复杂,很有可能导致其他问题。
比如img
的owner
不够明晰,出现多次对img
的delete
,就可能导致crash。这也是为什么用c++实现的大型项目,内存管理是一个十分让人头疼的问题。针对每个指针,都要明晰它的owner
并在不再使用时delete释放内存,小心再小心。
智能指针的出现给开发者带来了福音,用智能指针替换普通指针,开发者就不再需要手动释放内存了,也就不用担心忘记释放导致的内存泄漏,或者多次释放导致的crash问题了。
将PDFPage
修改为使用智能指针,就能同时解决内存泄漏和可能存在的闪退问题。
class PDFPage {
public:PDFPage(std::shared_ptr<QImage> _img);~PDFPage() = default;void paint();void addMark(std::shared_ptr<Mark> mark);// ...
private:std::shared_ptr<QImage> img;std::vector<std::shared_ptr<Mark>> marks;// ...
}