网站上传在空间哪里去了,新公司做网站多少钱,建设银行乾县支行网站,深圳市住房建设部网站内存泄漏
一、内存泄漏的危害#xff1a;
内存泄漏会导致当前应用程序消耗更多的内存#xff0c;使得其他应用程序可用的内存更少了。
如果有个进程可用的内存不够#xff0c;就会触发Linux操作系统的直接/后台内存回收#xff08;即将一些内存页的数据写到磁盘里#…内存泄漏
一、内存泄漏的危害
内存泄漏会导致当前应用程序消耗更多的内存使得其他应用程序可用的内存更少了。
如果有个进程可用的内存不够就会触发Linux操作系统的直接/后台内存回收即将一些内存页的数据写到磁盘里那么该页也就可用了脏页回写。虽然后台回收是异步的不阻塞当前进程但是内存还是不够会触发直接内存回收最后内存泄漏积累到一定程度会直接触发OOM该机制会杀掉那些实时占用内存大的进程。
而且即使没有OOM无论是直接回收还是后台回收都需要磁盘I/O而且需要多执行额外的回收线程使系统性能下降。 后台内存回收kswapd在物理内存紧张的时候会唤醒 kswapd 内核线程来回收内存这个回收内存的过程异步的不会阻塞进程的执行。直接内存回收direct reclaim如果后台异步回收跟不上进程内存申请的速度就会开始直接回收这个回收内存的过程是同步的会阻塞进程的执行这个过程比较慢导致CPU占用率飙升。 如果直接内存回收后空闲的物理内存仍然无法满足此次物理内存的申请那么内核就会放最后的大招了 ——触发 OOM Out of Memory机制。 还有资源泄漏
比如没有关闭文件程序提前return或报错或忘记关闭则可能导致想写入文件的数据没有真正落盘从而丢失数据。
二、内存泄漏举例
1在free()前就返回了或者是报错并退出程序。要在程序的所有路径上if()的各个条件都执行资源释放操作。
2在析构函数中未执行内存释放操作。在构造函数中申请了堆内存或者打开了文件在析构函数中忘了释放资源。
3基类的析构函数未声明为虚函数。
析构函数如果不声明为虚函数可能会导致多态对象在删除时无法正确调用派生类的析构函数如果子类构造函数里malloc()了内存然后在析构函数里free()从而导致内存泄漏。
4shared_ptr循环引用导致内存泄漏用weak_ptr解决。如下示例
class Contro {
private:double* p;public:Contro() {p new double[10];}~Contro() {delete[] p;std::cout in ~Contro std::endl;}
// 类内类class SubContro {public:SubContro() {p new double[10];}~SubContro() {delete[] p;std::cout in ~SubContro std::endl;}std::shared_ptrContro controller_;};std::shared_ptrSubContro sub_controller_;
};int main() {auto contro std::make_sharedContro();auto sub_contro std::make_sharedContro::SubContro();contro-sub_controller_ sub_contro;sub_contro-controller_ contro;// 打印引用计数std::cout contro use_count: contro.use_count() std::endl;std::cout sub_contro use_count: sub_contro.use_count() std::endl;return 0;
}发生循环引用两个的引用计数输出都是2所以main函数结束的时候引用计数没有减为0就不会调用二者的析构函数导致资源泄漏。 将SubContro类里的shared_ptr改成weak_ptr即可后者不会增加引用计数因此两个智能指针的引用计数都是1然后main结束的时候引用计数减少为0然后执行析构函数此时不会发生内存泄漏输出如下
contro use_count: 1
sub_contro use_count: 2
in ~Contro
in ~SubContro三、避免内存泄漏的手段
1. 静态代码检查工具
1对于大型项目可以使用啄木鸟这种静态代码分析工具 静态代码检查工具会从词法、语法、语义等多维度去对工程代码扫描分析发现可能存在的问题比如变量未定义、类型不匹配、变量作用域问题、数组下标越界、内存泄露等问题。 既然是静态那么就不是运行时。但是是编译前还是编译后还是编译中
其实都有啄木鸟是给源文件就行然后它会在编译的过程中去检测语法词法以及最后生成的二进制。 代码静态分析SAST可以简单的理解为在不执行程序的情况下对源代码 中间代码或者二进制代码进行分析的技术 2编译成专门的内存泄漏检查版本。
可以把整个项目编译成检查内存泄漏版本的可执行文件然后运行相关工具并且让运行结果专门记录内存泄漏将泄漏结果放在对应输出文件上。
比如opengauss就有参考链接http://t.csdn.cn/DqusO
编译openGauss时编译一个memcheck版的然后通过跑fastcheck_single来发现代码中的内存问题。 编译方式和编译普通的openGauss基本一致只是在configure时添加一个 --enable-memory-check 参数编译出来的就是memcheck版本的openGauss。
但是编译前要设置一些环境变量ulimit -v unlimited
ulimit命令用于控制shell程序的资源 -v 虚拟内存大小 指定可使用的虚拟内存上限单位为KB。
因为可能有内存泄漏所以就设置虚拟内存大小为不受限制。
2. valgrind工具
可以安装valgrind工具指定工具--toolmemcheck也可以指定输出日志否则输出在终端
--log-fileleak1.log
对可执行文件a.out执行如下命令
valgrind --log-filevalgrind.log --toolmemcheck --leak-checkfull --show-leak-kindsall ./a.out如下可以看到总的malloc和free的次数以及被申请的字节数在每一个内存泄漏的地方也会显示函数调用堆栈便于追踪 注图片相关函数做了打码处理
这个工具的用法还挺多可以参考https://zhuanlan.zhihu.com/p/92074597
3. GDB调试
比如我们怀疑FUNC()函数有内存泄漏。
1比如给某个函数FUNC()打断点进入后这个函数里面也调用了很多其他函数func1func2…怀疑这些调用里面或者外面有内存泄漏。我们可以给malloc()和free()打断点或者是自己封装的函数当malloc()命中后bt查看栈帧就知道哪个函数调用了malloc申请了堆内存比如func1这样可以重点关注该函数。
2然后看后面free()断点有没有命中命中的时候查看栈帧如果不是这个函数func1调用的free()那说明这个函数没有执行free。
3此外可以追踪指针p的值watch p看看它有没有变为0x0被释放且被赋值为0x0才不会成为悬空指针。
4在函数FUNC()的末尾还可以看看malloc和free的断点命中次数如果次数一样那没问题。