内存管理详解

news2024/11/18 21:30:12

目录

一、C/C++中内存分布

二、C语言的内存管理方式

三、C++的内存管理方式

3.1 new/delete操作内置类型

3.2 new/delete操作自定义类型

3.3 operator new()和operator delete()函数

3.4 重载operator new()和operator delete()

四、new、delete的实现原理

4.1 内置类型

4.2 自定义类型

五、定位new表达式

六、malloc/free和new/delete的区别

七、内存泄露

7.1 内存泄漏概念

7.2 内存泄漏分类

7.2.1 堆区内存泄漏

7.2.2 系统资源泄漏

7.3 内存泄漏的处理方式

7.3.1 事前预防

7.3.2 事后查错


一、C/C++中内存分布

 

 

二、C语言的内存管理方式

C语言的内存管理通常会用到malloc()、calloc()、realloc()、free()等函数,具体可以看我之前写过的一片博客。

(3条消息) C语言动态内存分配详解_GG_Bond19的博客-CSDN博客_c堆栈 分配原理icon-default.png?t=MBR7https://blog.csdn.net/GG_Bruse/article/details/124254856

三、C++的内存管理方式

3.1 new/delete操作内置类型

#include <iostream>
using namespace std;
int main()
{
	//动态开辟一个int类型大小的空间
	int* ptr1 = new int;
	delete ptr1;

	//动态开辟一个int类型大小的空间并初始化为10
	int* ptr2 = new int(10);
	cout << *ptr2 << endl;
	delete ptr2;

	//动态开辟10个int类型大小的空格键
	int* array = new int[10];
	delete[] array;

	return 0;
}

注意:

申请和释放单个元素的空间,使用new和delete操作符,申请和释放连续的空间,使用new[]和delete[]操作符。应匹配使用。

3.2 new/delete操作自定义类型

#include<iostream>
#include<cstdlib>
using namespace std;
class Test
{
public:
	Test(int data = 0): _data(data) { cout << "Test():" << this << endl; }
	~Test() { cout << "~Test():" << this << endl; }
private:
	int _data;
};
int main()
{
	Test* p1 = (Test*)malloc(sizeof(Test));
	free(p1);
	cout << endl;

	Test* p2 = new Test(1);
	delete p2;
	cout << endl;

	Test* p5 = (Test*)malloc(sizeof(Test) * 10);
	free(p5);
	cout << endl;

	Test* p6 = new Test[10];
	delete[] p6;

	return 0;
}

在申请自定义类型的空间时,new会调用构造函数初始化,delete会调用析构函数清理。因此使用new必须有可用的构造函数,而malloc与free不需要。

3.3 operator new()和operator delete()函数

new和delete是用户进行动态内存申请和释放的操作符,operator new 和operator delete是系统提供的全局函数,new在底层调用operator new全局函数来申请空间 + 构造函数,delete在底层通过operator delete全局函数来释放空间 + 析构函数。

注意: operator new与operator delete函数并不是对new、delete的操作符重载

operator new实际也是通过malloc来申请空间,若malloc申请空间成功就直接返回,否则执行用户提供的空间不足应对措施,若用户提供该措施就继续申请,否则就抛异常。operator delete最终也是通过free来释放空间的。

void* __CRTDECL operator new(size_t size) _THROW1(_STD bad_alloc)
{
	// try to allocate size bytes
	void* p;
	while ((p = malloc(size)) == 0)
		if (_callnewh(size) == 0)
		{
			//report no memory
			//若申请内存失败了,这里会抛出bad_alloc 类型异常
			static const std::bad_alloc nomem;
			_RAISE(nomem);
		}
	return (p);
}
void operator delete(void* pUserData)
{
	_CrtMemBlockHeader* pHead;
	RTCCALLBACK(_RTC_Free_hook, (pUserData, 0));
	if (pUserData == NULL)
		return;
	_mlock(_HEAP_LOCK); /* block other threads */
	__TRY
		/* get a pointer to memory block header */
		pHead = pHdr(pUserData);
	/* verify block type */
	_ASSERTE(_BLOCK_TYPE_IS_VALID(pHead->nBlockUse));
	_free_dbg(pUserData, pHead->nBlockUse);
	__FINALLY
		_munlock(_HEAP_LOCK); /* release other threads */
	__END_TRY_FINALLY
		return;
}
#define free(p) _free_dbg(p, _NORMAL_BLOCK)//free的实现

3.4 重载operator new()和operator delete()

一般情况下不需要对 operator new 和 operator delete进行重载,除非在申请和释放空间时候有某些特殊的需求。比如:在使用new和delete申请和释放空间时,打印一些日志信息,可以简单帮助用户来检测是否存在内存泄漏。

#include<iostream>
using std::cout;
using std::endl;
//重载operator new,在申请空间时:打印在哪个文件、哪个函数、第多少行,申请了多少个字节
void* operator new(size_t size, const char* fileName, const char* funcName, size_t lineNo)
{
	void* p = ::operator new(size);
	cout << fileName << "-" << funcName << "-" << lineNo << "-" << p << "-"<< size << endl;
	return p;
}
//重载operator delete
void operator delete(void* p)
{
	cout <<"delete->" << p << endl;
	free(p);
}
#ifdef _DEBUG//只有在Debug方式下,才调用用户重载的 operator new
#define new new(__FILE__, __FUNCTION__, __LINE__)
#endif
int main()
{
	int* p = new int;
	delete p;
	return 0;
}

重载了类专属的operator new()和operator delete(),就不会去调用全局的operator new()和operator delete()。

//重载类的专属operator new与operator delete
#include<iostream>
using namespace std;
class Test
{
public:
	void* operator new(size_t size)
	{
		cout << "void* operator new(size_t size)->STL内存池" << endl;
		void* obj = _alloc.allocate(1);
		return obj;
	}
	void operator delete(void* ptr)
	{
		cout << "void operator delete(void* ptr)->STL内存池" << endl;
		_alloc.deallocate((Test*)ptr,1);
	}
private:
	static allocator<Test> _alloc;//STL内存池
	int _data;
};
allocator<Test> Test::_alloc;
int main()
{
	//提高效率:申请时不会调用malloc,而是走定制的内存池
	Test* t1 = new Test;
	Test* t2 = new Test;
	Test* t3 = new Test;
	Test* t4 = new Test;
	delete t1;
	delete t2;
	delete t3;
	delete t4;
	return 0;
}

四、new、delete的实现原理

4.1 内置类型

若申请的是内置类型的空间,new和malloc,delete和free基本类似。
不同的地方是:new / delete申请和释放的是单个元素的空间,new[]和delete[]申请的是连续空间,而且new在申请空间失败时会抛异常,malloc则会返回NULL。

4.2 自定义类型

new的原理
1.调用operator new函数申请空间
2.在申请的空间上执行构造函数,完成对象的构造

delete的原理
1. 在空间上执行析构函数,完成对象中资源的清理工作
2. 调用operator delete函数释放对象的空间

new T[N]的原理
1. 调用operator new[]函数,operator new[]中实际调用operator new函数完成N个对象空间的申请
2. 在申请的空间上执行N次构造函数

delete[]的原理
1. 在释放的对象空间上执行N次析构函数,完成N个对象中资源的清理
2. 调用operator delete[]释放空间,实际在operator delete[]中调用operator delete来释放空间

五、定位new表达式

定位new表达式是在已分配的原始内存空间中调用构造函数初始化一个对象。

使用格式:
new (place_address) type或者new (place_address) type(initializer-list)
place_address必须是一个指针,initializer-list是类型的初始化列表

使用场景:
定位new表达式在实际中一般是配合内存池使用。因为内存池分配出的内存没有初始化,所以若是自定义类型的对象,则需要使用定位new表达式进行显示调构造函数进行初始化

#include<iostream>
using namespace std;
class Test
{
public:
	Test(int a = 0): _data(a) { cout << "Test():" << this << endl; }
	~Test() { cout << "~Test():" << this << endl; }
private:
	int _data;
};
int main()
{
	//p1现在指向的是与Test对象相同大小的一段空间,但构造函数没有执行
	Test* p1 = (Test*)malloc(sizeof(Test));
	new(p1)Test; // 注意:如果A类的构造函数有参数时,此处需要传参
	p1->~Test();
	free(p1);

	Test* p2 = (Test*)operator new(sizeof(Test));
	new(p2)Test(10);
	p2->~Test();
	operator delete(p2);
	return 0;
}

六、malloc/free和new/delete的区别

malloc/free和new/delete的共同点是:
都是从堆上申请空间,并且需要用户手动释放。

不同的地方是:
1. malloc和free是函数,new和delete是操作符
2. malloc申请的空间不会初始化,new可以初始化
3. malloc申请空间时,需要手动计算空间大小并传递,new只需在其后跟上空间的类型即可,如果是多个对象,[]中指定对象个数即可
4. malloc的返回值为void*, 在使用时必须强转,new不需要,因为new后跟的是空间的类型
5. malloc申请空间失败时,返回的是NULL,因此使用时必须判空,new不需要,但是new需要捕获异常
6. 申请自定义类型对象时,malloc/free只会开辟空间,不会调用构造函数与析构函数,而new在申请空间后会调用构造函数完成对象的初始化,delete在释放空间前会调用析构函数完成空间中资源的清理

七、内存泄露

7.1 内存泄漏概念

内存泄漏指因为疏忽或错误造成程序未能释放已经不再使用的内存的情况。内存泄漏并不是指内存在物理上的消失,而是应用程序分配某段内存后,因为设计错误,失去了对该段内存的控制,因而造成了内存的浪费。
内存泄漏的危害:长期运行的程序出现内存泄漏,影响很大,如操作系统、后台服务等等,出现
内存泄漏会导致响应越来越慢,最终卡死。

void MemoryLeaks()
{
    // 1.内存申请了忘记释放
    int* p1 = (int*)malloc(sizeof(int));
    int* p2 = new int;

    // 2.异常安全问题
    int* p3 = new int[10];
    Func(); // 这里Func函数抛异常导致 delete[] p3未执行,p3没被释放.
    delete[] p3;
}


 

7.2 内存泄漏分类

7.2.1 堆区内存泄漏

堆内存指的是程序执行中依据须要分配通过malloc / calloc / realloc / new等从堆中分配的一块内存,用完后必须通过调用相应的 free或者delete 删掉。假设程序的设计错误导致这部分内存没有被释放,那么以后这部分空间将无法再被使用,就会产生Heap Leak。

7.2.2 系统资源泄漏

指程序使用系统分配的资源,比方套接字、文件描述符、管道等没有使用对应的函数释放掉,导致系统资源的浪费,严重可导致系统效能减少,系统执行不稳定。

7.3 内存泄漏的处理方式

7.3.1 事前预防

1. 工程前期良好的设计规范,养成良好的编码规范,申请的内存空间记着匹配的去释放。
2. 采用RAII思想或者智能指针来管理内存资源
3. 有些公司内部规范使用内部实现的私有内存管理库。这套库自带内存泄漏检测的功能选项。

7.3.2 事后查错

出现问题后使用内存泄漏工具检测

在vs下有windows操作系统提供的_CrtDumpMemoryLeaks() 函数可以进行简单检测,但该函数只能报出了大概泄漏了多少个字节,并没有其他更准确的位置信息。

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

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

相关文章

(02)Cartographer源码无死角解析-(50) 2D点云扫描匹配→相关性暴力匹配2:RealTimeCorrelativeScanMatcher2D

讲解关于slam一系列文章汇总链接:史上最全slam从零开始&#xff0c;针对于本栏目讲解(02)Cartographer源码无死角解析-链接如下: (02)Cartographer源码无死角解析- (00)目录_最新无死角讲解&#xff1a;https://blog.csdn.net/weixin_43013761/article/details/127350885 文末…

【论文速递】TPAMI2022 - 小样本分割的整体原型激活

【论文速递】TPAMI2022 - 小样本分割的整体原型激活 【论文原文】&#xff1a;Holistic Prototype Activation for Few-Shot Segmentation 获取地址&#xff1a;https://ieeexplore.ieee.org/document/9839487 CSDN下载&#xff1a;https://download.csdn.net/download/qq_36…

三十、RabbitMQ(1)

&#x1f33b;&#x1f33b; 目录一、 关于中间件的概述二、基于消息中间件的分布式系统的架构2.1 消息中间件应用的场景2.2 常见的消息中间件2.3 消息中间件的本质及设计2.4 消息中间件的核心组成部分2.5 小总结三、消息队列协议3.1 什么是协议3.2 网络协议的三要素3.3 AMQP 协…

JAVA 23种设计模式示例

目录 一.单例模式 二.工厂方法模式 三.抽象工厂模式 四.建造者模式 五.原型模式 六.享元模式 七.门面模式 八.适配器模式 九.装饰者模式 十.策略模式 十一.模板方法模式 十二.观察者模式 十三.责任链模式 十四.代理模式 十五.桥接模式 十六.组合模式 十七.命令…

openGauss数据库PostGIS 安装与使用

目录 概述 1.PostGIS 安装 1.1 GCC-7.3编译器安装 1.2PostGIS依赖库安装 1.3.安装Postgis 2.使用Extension 2.1创建PostGIS Extension 2.2使用Extension 2.3删除Extension 概述 PostGIS Extension是PostgreSQL的空间数据库扩展&#xff0c;提供如下空间信息服务功能&…

SpringBoot+VUE前后端分离项目学习笔记 - 【21 权限菜单 中】

1 新建了sys_dict表以及相应Dict类保存菜单menu的icon数据 2 新建了sys_role_menu表以及相应RoleMenu类保存前端Role页面传来的角色菜单ID的绑定关系 3 在MenuController里增加获取Dict里icon的方法 提供前端菜单页面显示 4 在RoleController增加Post接口&#xff0c;获取前台传…

66页3万字医疗行业大数据治理解决方案

【版权声明】本资料来源网络&#xff0c;知识分享&#xff0c;仅供个人学习&#xff0c;请勿商用。【侵删致歉】如有侵权请联系小编&#xff0c;将在收到信息后第一时间删除&#xff01;完整资料领取见文末&#xff0c;部分资料内容&#xff1a; 目 录 1. 1、医疗行业大数据管…

分享116个PHP源码,总有一款适合您

PHP源码 分享116个PHP源码&#xff0c;总有一款适合您 116个PHP源码链接&#xff1a;https://pan.baidu.com/s/1dsupZiZbKqvHPmlpIAgWqA?pwdg52q 提取码&#xff1a;g52q import os import shutil import time from time import sleepimport requests from bs4 import Bea…

C++11静态断言static_assert

C11静态断言static_assert一、运行时断言二、静态断言的需求三、静态断言四、单参数版本的静态断言一、运行时断言 断言&#xff08;assertion&#xff09;是一种编程中常用的手段。在通常情况下&#xff0c;断言就是将一个返回值总是需要为真的判别式放在语句中&#xff0c;用…

Oracle No-Fee Terms and Conditions (NFTC)到底有啥条款?

1995年Sun微系统公司推出Java至今已有28年的历史&#xff0c;由于厂商持续升级优化&#xff0c;使用场景广阔&#xff0c;生态完善&#xff0c;Java目前仍然保持着非常旺盛的生命力。 付费许可 2019年java更新了许可政策 https://www.oracle.com/java/technologies/javase/ja…

【一文速通】机器学习样本不均衡/数据分布不同怎么办?

样本不均衡是什么意思样本&#xff08;类别&#xff09;样本不平衡&#xff08;class-imbalance&#xff09;指的是分类任务中不同类别的训练样例数目差别很大的情况&#xff0c;一般地&#xff0c;样本类别比例&#xff08;Imbalance Ratio&#xff09;&#xff08;多数类vs少…

antd中Tree组件使用方法个人笔记

一、前言 最近在自己自学前端&#xff0c;不清楚学习路线&#xff0c;只能盯着公司的前端项目硬看。 公司的前端项目是react框架&#xff0c;Ant Design Pro。 之前刚把router.config.js的逻辑理顺&#xff0c;目前准备开发个简单的前端页面。 在此总结下antd中<Tree>…

【算法刷题 DAY04】剑指offer树3和队列与栈总结

JZ36 二叉搜索树与双向链表 描述 输入一棵二叉搜索树&#xff0c;将该二叉搜索树转换成一个排序的双向链表。如下图所示 注意: 1.要求不能创建任何新的结点&#xff0c;只能调整树中结点指针的指向。当转化完成以后&#xff0c;树中节点的左指针需要指向前驱&#xff0c;树中…

虹科新闻 | 虹科与weeve正式建立合作伙伴关系

近日&#xff0c;虹科与weeve正式建立合作伙伴关系&#xff0c;双方就工业应用自动化领域进行深入的交流与合作&#xff0c;未来将共同致力于为中国市场提供完整的物联网边缘服务解决方案&#xff0c;解决中国客户的物联网挑战。 虹科与weeve都表示十分期待这次的合作。“虹科…

day36【代码随想录】贪心算法之根据身高重建队列、用最少数量的箭引爆气球、无重叠区间

文章目录前言一、根据身高重建队列&#xff08;力扣406&#xff09;二、用最少数量的箭引爆气球&#xff08;力扣452&#xff09;三、无重叠区间&#xff08;力扣435&#xff09;前言 1、根据身高重建队列 2、用最少数量的箭引爆气球 3、无重叠区间 一、根据身高重建队列&…

魔改插线板,让电视控制周边设备开关机

一.我的需求 本人是一个极简主义风格的人&#xff0c;自从用了N1盒子刷了coreELEC 系统后&#xff0c;就不断的进行折腾&#xff0c;跟大家说下我的心路历程。 1.我家很少看电视&#xff0c;不想因为偶尔开一次电视就每个月交24块钱&#xff0c;所以把广电的机顶盒停掉了。 2.电…

TextView

1.简介 向用户显示文本的用户界面元素。 2.常见使用 2.1 设置文本内容 //xml 硬编码 <TextView android:text"文本"/> //xml 推荐放在string.xml,为了国际化考虑 <TextView android:text"string/app_name"/> //kotlin tv.text getStr…

零基础学员的shell脚本的写作思路详解

前言 这两天一直再批改学员的脚本作业&#xff0c;大多数学员写的很好&#xff0c;有的学员写的不太好。 还有一些还没有入门到学员不知道脚本该咋写。 不知道脚本怎么写的学员&#xff0c;绝大多数犯了一个错误&#xff1a;一上来就把脚本想的太复杂了。 我们今天单独聊聊这…

以研究用途搭建OpenStreetMap Virtualbox服务器

又到了新年伊始&#xff0c;下载OpenStreetMap全球数据的时候了。结果惊奇的发现&#xff0c;主站已经无法打开。仔细了解了原委&#xff0c;表示理解。好在PBF数据依旧可以获取&#xff0c;只是瓦片服务已经关停。 1.OpenStreetMap的主要问题 OpenStreetMap之所以被Blocked&…

力扣刷题记录——459.重复的字符串、461. 汉明距离、476. 数字的补数

本专栏主要记录力扣的刷题记录&#xff0c;备战蓝桥杯&#xff0c;供复盘和优化算法使用&#xff0c;也希望给大家带来帮助&#xff0c;博主是算法小白&#xff0c;希望各位大佬不要见笑&#xff0c;今天要分享的是——《459.重复的字符串、461. 汉明距离、476. 数字的补数》。…