【玩转c++】c++内存管理 new/delete

news2024/11/16 15:36:24

 本期主题:c/c++内存管理。

  • 博客主页:小峰同学
  • 分享小编的在Linux中学习到的知识和遇到的问题
  • 小编的能力有限,出现错误希望大家不吝赐
  • 身为程序员 ,不会有人没有女朋友吧。

 


目录

🍁1. 了解c/c++内存区域划分

🍁2. C/C++内存分布

🍁3. C语言中动态内存管理方式

🍁4. C++中动态内存管理

🍁5. operator new与operator delete函数

🍁6. new和delete的实现原理

🍁7. 常见面试题

🍁8. 定位new表达式(placement-new)


  • 🍁1. 了解c/c++内存区域划分

首先我们要知道c/c++是直接跑在操作系统之上的,跑在操作系统上的程序叫进程,在c/c++程序中就会存在一个虚拟地址空间的一个东西(本质上是一种结构)存在进程PCB中,用于划分c/c++程序内存区域划分。

  • 我们写的代码是存在文件上的,我们写好的代码 经过编译链接汇编,形成机器指令。win下形成一个xxx.exe文件(linux形成一个xxx.out文件),这个文件叫程序(可执行程序),运行起来叫进程。

  • 🍁2. C/C++内存分布

先来看一段代码

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";//注意这里是将一个常量字符串拷贝给char2数组
     const char* pChar3 = "abcd";
     int* ptr1 = (int*)malloc(sizeof(int) * 4);
     int* ptr2 = (int*)calloc(4, sizeof(int));
     int* ptr3 = (int*)realloc(ptr2, sizeof(int) * 4);
     free(ptr1);
     free(ptr3);
}

1. 选择题:  

选项: A.栈  B.堆  C.数据段(静态区)  D.代码段(常量区)  

  • globalVar在哪里?__C__   staticGlobalVar在哪里?__C__  
  • staticVar在哪里?__C__   localVar在哪里?__A__  
  • num1 在哪里?__A__     char2在哪里?__A__  
  • *char2在哪里?_A__   pChar3在哪里?__A__      
  • *pChar3在哪里?__D__   ptr1在哪里?__A__        
  • *ptr1在哪里?_B___

2. 填空题:  

  • sizeof(num1) = __40__;    sizeof(char2) = __5__;      
  • strlen(char2) = __4__;   sizeof(pChar3) = __4/8__;    
  • strlen(pChar3) = __4__;   sizeof(ptr1) = _4/8___;

3. sizeof 和 strlen 区别? 

  • sizeof是一个关键字,strlen是一个函数。
  • sizeof是计算一个类型所占内存空间的大小 ,以字节为单位。
  • strlen是专门用于计算字符串长度的函数,计算的是字符串的有效字符,不包含'\0'
  • 详细信息观看:sizeof和strlen的区别

【说明】

  • 1. 栈又叫堆栈--非静态局部变量/函数参数/返回值等等,栈是向下增长的。
  • 2. 内存映射段是高效的I/O映射方式,用于装载一个共享的动态内存库。用户可使用系统接口 创建共享共享内存,做进程间通信。
  • 3. 堆用于程序运行时动态内存分配,堆是可以上增长的。
  • 4. 数据段--存储全局数据和静态数据。 5. 代码段--可执行的代码/只读常量。
  • 🍁3. C语言中动态内存管理方式

malloc/calloc/realloc/free

  • malloc申请指定大写的空间,字节为单位。 返回返回指向开辟空间的指针,注意这个指针(void*)类型的。
  • calloc申请指定类型多少个。(会初始化为0)
  • realloc扩容,给我们申请的内存扩容。有原地扩容和异地扩容两种。
  • 详细细节观看:C语言----动态内存管理,malloc,free,calloc,realloc
void Test ()
{
    int* p1 = (int*) malloc(sizeof(int));
    free(p1);
    // 1.malloc/calloc/realloc的区别是什么?
    int* p2 = (int*)calloc(4, sizeof (int));
    int* p3 = (int*)realloc(p2, sizeof(int)*10);

    // 这里需要free(p2)吗?
    //不需要,
    //因为realloc如果原地扩容,p3 == p2;
    //如果异地扩容realloc会帮我们释放掉原来的空间。

    free(p3 );
}

  • 🍁4. C++中动态内存管理

  • C语言内存管理方式在C++中可以继续使用,但有些地方就无能为力,而且使用起来比较麻烦,
  • 因 此C++又提出了自己的内存管理方式:通过new和delete操作符进行动态内存管理。
int main()
{
	int* p1 = new int;
	//没有初始化。
	delete p1;
	//也没有p1置空。最好手动置空。

	int* p2 = new int(0);
	//初始化版本
	delete p2;


	int* p3 = new int[10];//也不会初始化。
	//开辟多个int类型的连续空间。
	delete[] p3;
	//销毁的时候要加[];

	int* p4 = new int[10]{ 1,2,3,4,5 };
	//这里和数组的初始化一样的,初始化前5个后面的默认初始化为0
	delete[] p4;
	//注意这里不能分批销毁空间,只能一下子全部销毁。
	return 0;
}

注意:

  • 申请和释放单个元素的空间,使用new和delete操作符。
  • 申请和释放连续的空间,使用 new[]和delete[],注意:匹配起来使用。

 到这里人们就会想,这跟C语言有什么区别吗?甚至还没有C语言好用。

下面就让我们看看它和C语言有什么区别:

  • 相对于内置类型来说确实和C语言除了用法不同没有其他区别,可能还没有C语言好。
  • 但是相对于自定义类型来说就不一样了,new会调用自定义类型的构造函数,进行初始化。而malloc只是简单开辟空间,
  • delete会调用析构函数。free只是简单的销毁空间。不会管是否内存泄漏的问题。
  • mallo申请失败会返回NULL,一般需要自己检验是否申请失败
  • new失败了会抛异常,不需要我们自己检验是否开辟成功。
class A
{
public:
	A(int a = 0)
		:_a(a)
	{
		cout << "A():" << this << endl;
	}
	~A()
	{
		cout << "~A():" << this << endl;
	}
private:
	int _a;
};


int main()
{
	cout << "p1:" << endl;
	A* p1 = new A;//调用一次构造函数
	delete p1;//调用一次析构函数

	cout << "p2:" << endl;
	A* p2 = new A[10];//调用10次构造函数
	delete[] p2;//调用10次析构函数
	return 0;
}

运行结果:

 对自定义类型没有区别:

int main()
{
     // new/delete 和 malloc/free最大区别是 new/delete对于【自定义类型】除了开空间
    //还会调用构造函数和析构函数

     A* p1 = (A*)malloc(sizeof(A));
     A* p2 = new A(1);
     free(p1);
     delete p2;


     // 内置类型是几乎是一样的
     int* p3 = (int*)malloc(sizeof(int)); // C
     int* p4 = new int;
     free(p3);
     delete p4;


     A* p5 = (A*)malloc(sizeof(A)*10);
     A* p6 = new A[10];
     free(p5);
     delete[] p6;

     return 0;
}

注意:

  • 在申请自定义类型的空间时,new会调用构造函数,delete会调用析构函数,而malloc与 free不会。
  • 注意new和delete一定要匹配,否则会有一些意想不到的错误,也不能和 malloc/free混合起来用。都是标准未规范的。

  • 🍁5. operator new与operator delete函数

  • new和delete是用户进行动态内存申请和释放的操作符,operator new 和operator delete是 系统提供的全局函数,
  • new在底层调用operator new全局函数来申请空间。
  • delete在底层通过 operator delete全局函数来释放空间。
  • 注意这里 operator new 不是运算符的重载。
  • operator new 底层是使用了 malloc,为啥new不直接调用malloc,不符合new的机制,new失败了要抛异常。
  • 所以new的底层还是malloc,封装只是为了,符合面向对象的处理错误方式。错误抛异常。
/*
operator new:该函数实际通过malloc来申请空间,当malloc申请空间成功时直接返回;申请空间
失败,尝试执行空               间不足应对措施,如果改应对措施用户设置了,则继续申请,否
则抛异常。
*/
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);
}
/*
operator delete: 该函数最终是通过free来释放空间的
*/
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;
}
/*
free的实现
*/
#define   free(p)               _free_dbg(p, _NORMAL_BLOCK)

 我们看看 new 转换的汇编语言

new主要是为了完成两个事

  • 1.调用operator new函数
  • 2.调用构造函数

我们也可以直接调用 operator new ,这里operator new 和 malloc 调用差不多,但是失败不会返回NULL,而是直接抛异常。

但是自己一般不建议用 operator new。

总结: 

通过上述两个全局函数的实现知道,operator new 实际也是通过malloc来申请空间,如果 malloc申请空间成功就直接返回,否则执行用户提供的空间不足应对措施,如果用户提供该措施 就继续申请,否则就抛异常。operator delete 最终是通过free来释放空间的。


  • 🍁6. new和delete的实现原理

内置类型

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

自定义类型

 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来释 放空间
  • 🍁7. 常见面试题

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在释放空间前会调用析构函数完成 空间中资源的清理

 什么是内存泄漏,内存泄漏的危害

什么是内存泄漏:

  • 内存泄漏指因为疏忽或错误造成程序未能释放已经不再使用的内存的情况。内存泄漏并不是指内存在物理上的消失,而是应用程序分配某段内存后,因为设计错误,失去了对 该段内存的控制,因而造成了内存的浪费。

内存泄漏的危害

  • 长期运行的程序出现内存泄漏,影响很大,如操作系统、后台服务等等,出现 内存泄漏会导致响应越来越慢,最终卡死
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;
}

C/C++程序中一般我们关心两种方面的内存泄漏:

  • 堆内存泄漏(Heap leak) 堆内存指的是程序执行中依据须要分配通过malloc / calloc / realloc / new等从堆中分配的一 块内存,用完后必须通过调用相应的 free或者delete 删掉。假设程序的设计错误导致这部分 内存没有被释放,那么以后这部分空间将无法再被使用,就会产生Heap Leak。
  • 系统资源泄漏 指程序使用系统分配的资源,比如方套接字、文件描述符、管道等没有使用对应的函数释放掉,导致系统资源的浪费,严重可导致系统效能减少,系统执行不稳定。

如何检测内存泄漏:

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

int main()
{
     int* p = new int[10];
     // 将该函数放在main函数之后,每次程序退出的时候就会检测是否存在内存泄漏
     _CrtDumpMemoryLeaks();
     return 0;
}




// 程序退出后,在输出窗口中可以检测到泄漏了多少字节,但是没有具体的位置
Detected memory leaks!
Dumping objects ->
{79} normal block at 0x00EC5FB8, 40 bytes long.
Data: <                > CD CD CD CD CD CD CD CD CD CD CD CD CD CD CD CD
Object dump complete.

因此写代码时一定要小心,尤其是动态内存操作时,一定要记着释放。但有些情况下总是防不胜 防,简单的可以采用上述方式快速定位下。如果工程比较大,内存泄漏位置比较多,不太好查时 一般都是借助第三方内存泄漏检测工具处理的。

在linux下内存泄漏检测:linux下几款内存泄漏检测工具

在windows下使用第三方工具:VLD(Visual LeakDetector)内存泄露库

其他工具:内存泄漏工具比较


  • 🍁8. 定位new表达式(placement-new)

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

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

使用场景:

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

class A
{
public:
	A(int a = 0)
		: _a(a)
	{
		cout << "A():" << this << endl;
	}
	~A()
	{
		cout << "~A():" << this << endl;
	}
private:
	int _a;
};

// 定位new/replacement new
int main()
{
	// p1现在指向的只不过是与A对象相同大小的一段空间,还不能算是一个对象,因为构造函数没有执行
	A* p1 = (A*)malloc(sizeof(A));
	// 注意:如果A类的构造函数有参数时,此处需要传参
	//构造函数 一般只能隐式调用在创建变量,或者开辟空间的时候自动调用,都是隐式调用,不能显示调用,
	// 
	//new(p1)A(1);  //注意他的用法
	new(p1)A();  //也可也无参
	p1->~A();
	//构造函数不能显示调用,必须用定位new表达式,但是析构函数可以显示调用。
	free(p1);

	A* p2 = (A*)operator new(sizeof(A));//operator new只是完成开辟空间
	//未调用构造函数。
	new(p2)A(10);//显示调用构造函数

	p2->~A();//显示调用析构函数
	operator delete(p2);//同理operator delete 也没有调用析构函数,只是完成释放空间这一步骤。
	return 0;
}

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

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

相关文章

hbuilderx升级3.6.5版本后运行到手机端同步资源失败,未得到同步资源的授权,请停止运行后重新运行,并注意手机上的授权提示

问题1&#xff1a; hbuilderx升级3.6.5版本后运行到手机端同步资源失败&#xff0c;未得到同步资源的授权&#xff0c;请停止运行后重新运行&#xff0c;并注意手机上的授权提示。 解决问题指路。 https://uniapp.dcloud.net.cn/tutorial/run/run-app-faq.html#node https://un…

在线考试系统毕业设计,线上考试系统设计与实现,毕业设计论文源码开题报告需求分析

项目背景和意义 目的&#xff1a;本课题主要目标是设计并能够实现一个在线考试的java系统&#xff0c;整体使用了基于浏览器的B/S架构&#xff0c;技术上使用了基于java的springboot框架&#xff1b;使用浏览器&#xff0c;通过后台添加考试题目&#xff0c;学生通过浏览器登录…

一文解决网络系统调用接口到内核的请求

网络套接字入口函数 //所有的网络套接字系统调用函数&#xff08;socket bind listen connect &#xff09;都使用一个共同的入口函数&#xff1a;sys_socketcall /* 第一个参数call表示被调用的应用层接口函数&#xff0c;第二个参数是一个指针&#xff0c;指向具体被调用函数…

【综合案例】原生JS实现购物商城

目录一、案例说明1、目录结构2、conf文件夹3、用户名密码的正则和ajax的封装二、登录页的实现1、案例效果2、登录页逻辑3、接口文档4、代码实现5、返回信息显示三、首页的实现1、案例效果2、首页的逻辑3、接口文档4、代码实现5、返回信息显示四、个人中心1、案例效果2、个人页的…

springboot车辆管理系统的设计与实现毕业设计源码031034

车辆管理系统的设计与实现 摘 要 科技进步的飞速发展引起人们日常生活的巨大变化&#xff0c;电子信息技术的飞速发展使得电子信息技术的各个领域的应用水平得到普及和应用。信息时代的到来已成为不可阻挡的时尚潮流&#xff0c;人类发展的历史正进入一个新时代。在现实运用中&…

Runtime源码解析-alloc

Runtime源码解析-alloc 前言alloc 通过汇编查看调用流程1. objc_alloc方法2. callAlloc方法 首次进入非首次进入LLVM优化 3. _objc_rootAllocWithZone方法4. _class_createInstanceFromZone方法 instanceSize&#xff1a;计算内存大小 fastInstanceSizealignedInstanceSize mal…

TS201的DMA通信基本原理以及IIR的加深理解(含源代码)

实验目的&#xff1a; 了解DMA通信基本原理&#xff0c;掌握内存与SDRAM间一维DMA通信方式、二维DMA通信方式以及相关控制方法。学习数字滤波器设计方法&#xff0c;掌握其调试步骤&#xff0c;使学生加深对IIR的理解&#xff0c;进一步提高对数字信号处理理论的认识。 实验任…

Java+SSM美妆商城全套电商购物(含源码+论文+答辩PPT等)

项目功能简介: 本项目含代码详细讲解视频&#xff0c;手把手带同学们敲代码从0到1完成项目 该项目采用技术Springmvc、Spring、MyBatis、Tomcat服务器、MySQL数据库 项目含有源码、配套开发软件、软件安装教程、项目发布教程以及代码讲解教程 项目功能介绍&#xff1a; 系统管理…

[附源码]计算机毕业设计酒店物联网平台系统Springboot程序

项目运行 环境配置&#xff1a; Jdk1.8 Tomcat7.0 Mysql HBuilderX&#xff08;Webstorm也行&#xff09; Eclispe&#xff08;IntelliJ IDEA,Eclispe,MyEclispe,Sts都支持&#xff09;。 项目技术&#xff1a; SSM mybatis Maven Vue 等等组成&#xff0c;B/S模式 M…

国标码的发展历史以及编码方式

文章目录引言GB2312GBKunicode 和 ISO10646ISO10646的编码结构结论参考文献引言 由于物理实现比较容易等原因计算机等数字系统内部使用二进制字符的记录、存贮、传递和交换通过编码来实现。字符的机内编码其实就是该字符在字符图库中的序号。拼音文字一般仅有几十个字母组成。而…

【传输层】TCP、三次握手、四次挥手、可靠传输、TCP拥塞控制、慢开始、拥塞避免、快重传、快恢复

文章目录TCP------打电话----可靠有序、不丢不重复--------提供全双工-------------发送接收缓存----------面向字节流--------搬砖一样加个头运走TCP首部格式-----源端口目的端口一共4B-------序号字段&#xff08;报文第一个字节的序号&#xff09;--------确认号&#xff08…

手机软件系统测试用例设计大全

一、 等价类分析法 二、 边界值分析 三、 错误猜测法 四、 判定表法 五、 流程分析方法 六、 正交试验设计法 七、 状态迁移法 等价类分析法等价类划分方法针对手机状态大致可以归几个大类&#xff1a; 按键类&#xff08;等价法&#xff09;&#xff1a;有效输入和无效…

python在Keras中使用LSTM解决序列问题

时间序列预测是指我们必须根据时间相关的输入来预测结果的问题类型。时间序列数据的典型示例是股市数据&#xff0c;其中股价随时间变化。 最近我们被客户要求撰写关于LSTM的研究报告&#xff0c;包括一些图形和统计输出。 递归神经网络&#xff08;RNN&#xff09;已被证明可…

D-026 LVDS硬件电路设计

LVDS硬件接口电路设计1 简介2 硬件设计实战3 硬件设计要点4 Layout注意事项5 MIPI与LVDS的区别1 简介 LVDS&#xff08;Low-Voltage Differential Singnaling&#xff0c;低电压差分信号&#xff09;可以实现点对点或者一点对多点的连接&#xff0c;具有低功耗、低误码率、低串…

蓝桥杯比赛 NOC竞赛C++项目,选择题真题和模拟题汇总答案解析

题目来源&#xff1a;第10届蓝桥杯青少年组C选拔赛 1、下面哪个密码最安全 D A. 111111 B. 123456 C. qwerty D. Z2a8Q1 2、如果今天是星期六&#xff0c;再过60天是星期几&#xff1f;A A. 星期三 B. 星期四 C. 星期五 D. 星期六 3、90到100之间有几个素数&#xff1f…

HTML学生作业网页 传统端午节节日 学生节日网页设计作业源码(HTML+CSS+JS)

&#x1f389;精彩专栏推荐 &#x1f4ad;文末获取联系 ✍️ 作者简介: 一个热爱把逻辑思维转变为代码的技术博主 &#x1f482; 作者主页: 【主页——&#x1f680;获取更多优质源码】 &#x1f393; web前端期末大作业&#xff1a; 【&#x1f4da;毕设项目精品实战案例 (10…

Android Studio的ADV无法启动解决办法【IDEA 的ADV无法启动解决办法】

Android Studio的ADV无法启动解决办法【IDEA 的ADV无法启动解决办法】 开发者指南:https://developer.android.google.cn/guide 一、BIOS开启Intel VT-x 这一步如果在创建Android项目时&#xff0c;可以启动的话&#xff0c;证明已经开启了&#xff0c;可以忽略 1. 开始菜单&am…

新鲜出炉!阿里内部开源SpringCloud Alibaba全解(全彩版)全网首发

第一章微服务介绍 第二章微服务环境搭建 第三章Nacos Discovery–服务治理 自定义实现负载均衡 第四章Sentinel–服务容错 第五章Gateway–服务网关 Gateway核心架构 第六章Sleuth-链路追踪 ![新鲜出炉&#xff01;阿里内部开源SpringCloud Alibaba全解&#xff08;全彩版&…

基于OpenLayers实现导航地图上(起/终)点的交互式图标显示

目录 1、准备 2、瓦片地图显示 3、增加矢量图层 4、利用click实现鼠标点击效果 在常见的导航软件中&#xff0c;往往都存在标记起/止点的需求。毕竟路径规划中的重要传入参数就是起止点坐标。在常用的不管是移动端还是PC端&#xff0c;导航地图上一般在选择起止点位置会留…

大数据 | Spark安装及测试

一、安装 Spark On Yarn 在公司中&#xff0c;通常采用Yarn进行资源调度&#xff0c;故此处采用Yarn模式的集群部署。 采用Yarn部署模式时&#xff0c;需要保证集群中已经安装好Hadoop集群&#xff0c;在此基础上才能实现Yarn模式的部署。 在Yarn模式中&#xff0c;Spark应用…