【C++】内存泄漏 智能指针

news2024/9/28 18:56:40

目录

  • 一、什么是内存泄漏
  • 二、如何检测内存泄漏
    • 1、内存占用变化排查法
    • 2、valgrind定位法
    • 3、mtrace定位法
  • 三、智能指针分类及作用
    • 1、unique_ptr
    • 2、shared_ptr
    • 3、weak_ptr

一、什么是内存泄漏

在实际的 C++ 开发中,我们经常会遇到诸如程序运行中突然崩溃、程序运行所用内存越来越多最终不得不重启等问题,这些问题往往都是内存资源管理不当导致的。比如:

  • 有些内存资源已经被释放,但指向它的指针并没有改变指向(成为了野指针),并且后续还在使用;
  • 有些内存资源已经被释放,后期又试图再释放一次(重复释放同一块内存会导致程序运行崩溃);
  • 没有及时释放不再使用的内存资源,造成内存泄漏,程序占用的内存资源越来越多。

参考链接:C/C++什么是内存泄露,内存泄露如何避免?

二、如何检测内存泄漏

1、内存占用变化排查法

内存泄漏一般不会造成程序崩溃,所以比较隐晦,但是发现内存泄露的方法也很简单,就是让程序运行一段时间,然后查看内存先后变化,通过任务管理器(windows)或者top(unix/linux)来监控某个进程的内存变化是比较方便的,有些程序的内存泄露比较小,但是发现它的内存泄露也都是时间问题。这里列出一个内存泄漏的程序的内存变化时间图,可以看出其内存占用总体上是呈递增的
在这里插入图片描述
内存泄漏较大的情况下,机器cpu使用率飙升,cpu的wait百分比增加,通过top可以看到swap内存使用量不断增加,kswap进程不时出现在进程列表当中。
linux中可以通过watch -n1 "ps -o vsz -p <PID>",实时看到特定进程的内存使用量不断地增加

2、valgrind定位法

debian/ubuntu派系的linux下安装使用方法:

sudo apt install valgrind
valgrind ./main

参考链接:valgrind排查内存泄露

3、mtrace定位法

#include <mcheck.h>
 
int main(int argc, char **argv)
{
   setenv("MALLOC_TRACE","output",1);
 
   mtrace();
}

运行程序之后,在程序的当前目录下会生成output文件,然后使用命令获取堆栈信息:

mtrace [程序名] output > msg.txt

通过查看msg.txt文件,就可以找到内存泄漏的地方、大小,如:

Memory not freed:
-----------------
           Address     Size     Caller
0x0000000001ed4760     0x18  at 0x7fface2c1780
0x0000000001ed47b0     0x18  at 0x7fface2c1780
0x0000000001ed59f0     0xb0  at 0x7fface2c1780
0x0000000001ed5ab0     0x18  at 0x7fface2c1780
0x0000000001ed5ad0     0x18  at 0x7fface2c1780
0x0000000001ed5af0     0x18  at 0x7fface2c1780

参考链接:c/c++程序内存泄漏跟踪总结

三、智能指针分类及作用

  为了避免内存泄漏,我们需要将每一个malloc和delete, new和free对应起来,显然这十分地麻烦而且容易出错,那是否有更简便的方式呢?
  试想如果我们将分配的动态内存都交由有生命周期的对象来处理,在它的析构函数中删除指向的内存,那么在对象过期时,是不是就可以自动的释放内容从而避免内存泄漏呢!
  答案是肯定的,智能指针就使用了这种思想来帮助我们C++程序员管理动态分配的内存的,它会帮助我们自动释放new出来的内存,从而避免内存泄漏!

  • C++98/03 中,支持使用 auto_ptr 智能指针来实现堆内存的自动回收;
  • C++11 中废弃了 auto_ptr,支持使用 unique_ptr、shared_ptr 以及 weak_ptr 这 3 个智能指针来实现堆内存的自动回收。
// 头文件
#include <memory>

参考链接: C++ 智能指针 - 全部用法详解

1、unique_ptr

特点:

  • 无法同其它 unique_ptr 共享,一旦该 unique_ptr 指针放弃对所指堆内存空间的所有权,则该空间会被立即释放回收。
  • 无法进行左值unique_ptr复制构造,也无法进行左值复制赋值操作,但允许临时右值赋值构造和赋值
  • 保存指向某个对象的指针,当它本身离开作用域时会自动释放它指向的对象。
  • 在容器中保存指针是安全的

适用场景:
动态申请的内存仅在当前作用域有效

void func(){
	int * p = new int(1111);
	/*do something*/
	delete p;
}

  如果在do something的时候,出现了异常,退出了,那delete就永远没有执行的机会,就会造成内存泄露,而如果使用unique_ptr就不会有这样的困扰了。而相比于shared_ptr,它的开销更小,甚至可以说和裸指针相当,它不需要维护引用计数的原子操作等等。所以说,如果有可能,优先选用unique_ptr。

void func(){
	std:unique_ptr p( new int(1111) );
	/*do something*/
}

用法:
在这里插入图片描述

// 创建
std::unique_ptr<int> p1();
std::unique_ptr<int> p2(nullptr);
std::unique_ptr<int> p3(new int(10));
std::unique_ptr<int> p4(p3);//错误,堆内存不共享
std::unique_ptr<int> p5(std::move(p3));//正确,调用移动构造函数

// 赋值
unique_ptr<Test> t7(new Test);
unique_ptr<Test> t8(new Test);
t7 = std::move(t8);	// 必须使用移动语义,结果,t7的内存释放,t8的内存交给t7管理

// 主动释放对象
unique_ptr<Test> t9(new Test);
t9 = NULL;
t9 = nullptr;
t9.reset();

// 放弃对象控制权
Test *t10 = t9.release();

// 重置
t9.reset(new Test)。

参考链接:

  1. 为何优先选用unique_ptr而不是裸指针?
  2. C++11 unique_ptr智能指针详解

2、shared_ptr

特点:
  多个 shared_ptr 智能指针可以共同使用同一块堆内存。并且,由于该类型智能指针在实现上采用的是引用计数机制,即便有一个 shared_ptr 指针放弃了堆内存的“使用权”(引用计数减 1),也不会影响其他指向同一堆内存的 shared_ptr 指针(只有引用计数为 0 时,堆内存才会被自动释放)。
适用场景:
  当动态申请的内存被建立多个索引时,常常会触发一处索引释放了内存地址,而另一处索引又请求内存地址导致的异常。这时我们可以使用shared_ptr对地址空间进行托管,shared_ptr会自动记录指向当前地址的索引,当索引全部删除时会自动释放内存地址,非常的方便!

用法:

std::shared_ptr<int> p3(new int(10));
std::shared_ptr<int> p3 = std::make_shared<int>(10);
//调用拷贝构造函数
std::shared_ptr<int> p4(p3);
std::shared_ptr<int> p4 = p3;
//调用移动构造函数
std::shared_ptr<int> p5(std::move(p4)); //或者 std::shared_ptr<int> p5 = std::move(p4);

// 显示引用计数
p5.use_count()

std::move(p4) 来说,该函数会强制将 p4 转换成对应的右值,因此初始化 p5 调用的是移动构造函数。另外和调用拷贝构造函数不同,用 std::move(p4) 初始化 p5,会使得 p5 拥有了 p4 的堆内存,而 p4 则变成了空智能指针

对于申请的动态数组来说,shared_ptr 指针默认的释放规则是不支持释放数组的,只能自定义对应的释放规则,才能正确地释放申请的堆内存。

//自定义释放规则
void deleteInt(int*p) {
    delete []p;
}
//初始化智能指针,并自定义释放规则
std::shared_ptr<int> p7(new int[10], deleteInt);
// lambda 表达式
std::shared_ptr<int> p7(new int[10], [](int* p) {delete[]p; });

参考链接: C++11 shared_ptr智能指针(超级详细)

3、weak_ptr

  1. 弱指针用于接管共享指针,在需要使用时可以转换成共享指针。
  2. 当被接管的共享指针失效时,弱指针随即失效。
  3. 弱指针不支持 * 和 -> 对指针的访问;

示例代码:

     std::weak_ptr<int> gw;
    {
		auto sp = std::make_shared<int>(42);
		gw = sp;
		// expired:判断当前智能指针是否还有托管的对象,有则返回false,无则返回true
        if (!gw.expired()) {
            std::cout << "gw is valid\n";	// 有效的,还有托管的指针
        } else {
            std::cout << "gw is expired\n";	// 过期的,没有托管的指针
        }

        auto gw_sp = gw.lock();	// lock:返回一个shared_ptr对象,指向被托管的对象,如果没有则返回空的shared_ptr对象
        std::cout << *gw_sp << std::endl;
	}

	// 当{ }体中的指针生命周期结束后,再来判断其是否还有托管的指针
    if (!gw.expired()) {
        std::cout << "gw is valid\n";	// 有效的,还有托管的指针
    } else {
        std::cout << "gw is expired\n";	// 过期的,没有托管的指针
    }

gw is valid
42
gw is expired

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/546334.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

关于 HTTPS 的加密流程

目录 HTTP 与 HTTPS 的区别加密方式HTTPS 基本工作过程1. 仅使用对称密钥2. 引入非对称密钥对 key 进行加密3. 引入证书, 破解中间人攻击 HTTP 与 HTTPS 的区别 其实 HTTPS 与 HTTP 一样都是应用层协议, HTTPS 只是在 HTTP 的基础上再加上了一个加密层. 为啥要对 HTTP 进行加密…

bugku---misc

一.telnet 下载后是一个压缩包 条件反射&#xff0c;先丢在wireshark中看一下&#xff0c; 直接搜flag&#xff0c;就 出来了 Data: flag{d316759c281bf925d600be698a4973d5} 二.简单取证1 之前只做过取证大赛的&#xff0c;但是这个没有啥思路&#xff0c;看了一下需要工具m…

Redis集群简介及槽位映射(哈希取余和一致性哈希算法)

redis cluster需求至少需要3个master才能组成一个集群&#xff0c;同时每个sentinel至少有一个slave节点&#xff0c;各个节点之间保持tcp通信。当master发生宕机&#xff0c;redis cluster自动将对应的slave节点提拔为master,来重新对外提供服务。 先来说一下槽&#xff0c;集…

从“被动发现”变为“主动感知”|智能井盖脚下安全守护者

井盖作为城市基础设施的重要组成部分&#xff0c;具有关键的作用。城市的管道网络错综复杂&#xff0c;包括雨水、污水、弱电和强电等。其中&#xff0c;雨水和污水管道的管径较大&#xff0c;可能会导致隐藏或其他安全事故的发生。而弱电井则是整个城市信息传输的重要环节&…

Java 基础进阶篇(十四):File 类常用方法

File 类的对象代表操作系统的文件&#xff08;文件、文件夹&#xff09;&#xff0c;File 类在 java.io.File 包下。 File 类提供了诸如&#xff1a;创建文件对象代表文件&#xff0c;获取文件信息&#xff08;大小、修改时间&#xff09;、删除文件、创建文件&#xff08;文件…

Java【文件和IO】File 类, 字节IO流的使用

文章目录 前言一、File 类1, 构造方法2, 成员方法 二、字节流输入输出1, 字节流输入 InputStream1.1, 每次输入一个字节1.2, 每次输入多个字节 2, 字节流输出 OutputStream2.1, 每次输出一个字节2.2, 每次输出多个字节 总结 前言 各位读者好, 我是小陈, 这是我的个人主页, 希望…

chatgpt赋能Python-python3__2怎么算

Python3中<<2的计算方法 Python3是一种高级编程语言&#xff0c;它具有强大的数据分析和计算能力。在Python3中&#xff0c;<<2是一种用于移位计算的运算符。在本篇文章中&#xff0c;我们将介绍Python3中<<2的计算方法。 什么是移位运算符 移位运算符是一…

C++基础语法——内存管理

1. C/C中的内存管理 我们先看如下一段代码 #include <iostream>using namespace std;int globalVar 1; static int staticGlobalVar 1;void Test() {static int staticVar 1;int localVar 1;int num1[10] { 1, 2, 3, 4 };char char2[] "abcd";const ch…

rk3399 buildroot ubuntu20版本编译遇到问题

一、编译uboot遇到问题 /usr/include/libfdt.h:258:1: error: redefinition of fdt_set_version 258 | fdt_set_hdr_(version); | ^~~~~~~~~~~~ In file included from tools/fdt_host.h:11, from tools/imagetool.h:22, from tools…

BurpSuite—-Target模块(目标模块)

前言 本文主要介绍BurpSuite—-Target模块(目标模块)的相关内容 关于BurpSuite的安装可以看一下之前这篇文章&#xff1a; http://t.csdn.cn/cavWt Target功能 目标工具包含了SiteMap&#xff0c;用你的目标应用程序的详细信息。它可以让你定义哪些对象在范围上为你目前的工…

基于QGIS的长株潭城市群边界范围融合实战

背景 在面向区域的研究过程中&#xff0c;比如一些研究区域&#xff0c;如果是具体的行政区划&#xff0c;比如具体的某省或者某市或者县&#xff0c;可以直接从国家官方的地理数据中直接下载就可以。但如果并没有直接的空间数据那怎么办呢&#xff1f;比如之前遇到的一个场景&…

chatgpt赋能Python-python3_len__

Python3 len()函数&#xff1a;使用方法和常见应用 在Python中&#xff0c;len()函数是一个内置函数&#xff0c;用于返回对象的长度或元素的个数。它适用于字符串、列表、元组、字典、集合等Python数据类型。在本篇文章中&#xff0c;我们将深入探讨Python3 len()函数的使用方…

C++ [STL之string模拟实现]

本文已收录至《C语言和高级数据结构》专栏&#xff01; 作者&#xff1a;ARMCSKGT STL之string模拟实现 前言正文存储结构默认成员函数构造函数析构函数拷贝构造函数赋值重载 容量操作类获取字符串长度size获取当前容量capacity查询是否为空串emptyreserve扩容调整字符串大小si…

基于Freertos的ESP-IDF开发——6.使用DHT1温湿度传感器

基于Freertos的ESP-IDF开发——6.使用DHT1温湿度传感器 0. 前言1. DHT11驱动原理2. 完整代码3. 演示效果4. 其他FreeRtos文章 0. 前言 开发环境&#xff1a;ESP-IDF 4.3 操作系统&#xff1a;Windows10 专业版 开发板&#xff1a;自制的ESP32-WROOM-32E 准备一个DHT11温湿度传…

【Element-ui】el-table大数据量渲染卡顿问题

1、场景描述 在项目开发中&#xff0c;遇到在表格中一次性加载完的需求&#xff0c;且加载数量不少&#xff0c;有几百几千条&#xff0c;并且每条都可能有自己的下拉框&#xff0c;输入框来做编辑功能&#xff0c;此时普通的el-table肯定会导致浏览器卡死&#xff0c;那么怎么…

Windows10 双网卡配置,轻松实现内外互通

1 背景 我平常需要连接外网查阅资料&#xff0c;聊天等&#xff0c;做实验时需要将写到的代码打包部署到内网服务器中&#xff0c;所以需要频繁地切换内外网&#xff0c;修改静态网络配置。很是苦恼。 2 方法 最近&#xff0c;我找到了几种解决办法。 方法一、…

uniapp DIY可视化工具 控件拖拽工具怎么开发/拖拽库/模板引擎

uniapp DIY可视化工具 控件拖拽工具怎么开发 答&#xff1a;需要 (**拖拽库**) &#xff08;**模板引擎**&#xff09;代码自动生成 python有哪些模板引擎 Jinja2是一个小型但快速且易于使用的以纯python编写的独立模板引擎&#xff08;最主要的模板&#xff09;Chameleon是T…

深度学习之卷积神经网络(CNN)

大家好&#xff0c;我是带我去滑雪&#xff01; 卷积神经网络&#xff08;Convolutional Neural Network&#xff0c;CNN&#xff09;是一种基于深度学习的前馈神经网络&#xff0c;主要用于图像和视频识别、分类、分割和标注等计算机视觉任务。它主要由卷积层、池化层、全连接…

【开源、应用】QT—TCP网络上位机的设计

本文设计一个终端控制的上位机软件&#xff08;如“设计目标”下图所示&#xff09;&#xff0c;可以和STM32、Adruino等通信实现无线局域网控制系统。 本文的通信内容和图表内容可以参考作者之前的文章 STM32ESP8266连接电脑Qt网络上位机——QT篇https://blog.csdn.net/qq_53…

【STM32G431RBTx】备战蓝桥杯嵌入式→决赛试题→第六届

文章目录 前言一、题目二、模块初始化三、代码实现interrupt.h:interrupt.c:main.h:main.c: 四、完成效果五、总结 前言 无 一、题目 二、模块初始化 1.LCD这里不用配置&#xff0c;直接使用提供的资源包就行 2.ADC:开启ADCsingle-ended 3.LED:开启PC8-15,PD2输出模式就行了…