00:00
好同学们,我们刚才呢,把那个沙Le呢给大家说完了啊,其实我们主要呢,是看了一下它基本的一些源码,当然了,我们很多的源码没有看那么细啊,为什么呢?因为都是偏向于底层,什么包括排序,包括底层的数据的,呃,读取,包括合并这些东西,我们只要知道它的原理是什么,其实就够了啊,因为这些细节都是由框架帮你完成的,你多多少少呢可以调整一些参数来优化它,对吧?但是呢,你说你自己写一个这个不太现实,对不对,所以把原理搞明白,把那些参数呢,大概知道它的一些含义就够了,但是除了商可以把我们的这个就是嗯,通过修改一些参数来优化以外,其实还有一点就是内存。啊,因为我们这个Spark框架本身就是基于map,是只不过优化了其中的计算过程,把它的那个落盘的数据放在了什么内存当中,在作业执行之间,用内存来改善了我们磁盘的这种IO,所以性能得到了提高,所以内存的管理对于SPA来讲非常的重要啊,那么我们接下来呢,就给大家看一看这个10SPAUG中内存管理的源码的位置啊,咱们可以大概的看一看,首先呢,咱们先给大家画一个图啊,为什么画图呢?因为我们Spark的内存啊,它里面也分一些我们的模块,这个咱们要说一下了啊,简单的咱们描述一下在Java当中,Java虚拟机啊,Java虚拟机它的内存呢,分这么几块,它分什么呢?我们的这个栈对不对,同学们没问题吧,然后呢,分成什么呢?我们的堆啊,咱们叫堆栈嘛,那还有我们的方法区,这个没问题吧,同学们,诶,咱们叫方法区,嗯,在有些版本当中称之为叫圆空间,叫Meta space,对不对,差不多。
01:41
啊,咱们知道这个含义,这个呢是我们的占空间啊来,然后呢,这个呢是我们的堆空间,嗯,咱们的堆空间,这个是我们的方法区空间,然后呢,我们的占空间主要是在方法执行的时候压站,那个战争里面放置的一些什么临时这个我们的呃,局部变量表啊,或者本地变量啊,包括一些数据,以及一些我们方法的执行操作对不对,然后堆里面放的是动态创建的对象,一般都会放里面,方法区一般放的都是一些静态的呀,类的信息都是在里面,对不对,所以每块内存放的东西是不一样的,它会分一个类啊在里面。
02:16
然后呢,还有一个就是我们这个堆空间呀,它有个垃圾回收的概念,那么这个垃圾回收呢,你要是把整个堆回收也有点麻烦,所以在这种情况下,这个堆呀,它其实也分了几块,记住啊,同学们,它为了方便管理这个堆也不是一个完整的操作,这个堆也分了几块,分什么呢?诶比方说大家应该知道有个叫新生代,对不对,咱们叫新生代啊,咱们就不画那么细了,包括什么伊甸园的幸存者呀,这些东西咱们就不画那么细了,还有什么东西啊,是不是还应该有一个老年代啊,对不对,还有一个老年代啊,咱们叫老年代,嗯。好了啊,所以呢,我们的加热虚拟机当中那个垃圾回收呢,其实是一个分代管理的,什么意思呢?它里面可以对新生代做垃圾回收,是不是也可以对我们的老代做垃圾回收啊,如果对新生代的垃圾回收,我们称之为叫G,对我们的老年代的垃圾回收,其实当然也包括新生代了,我们叫做什么major GC叫做负GC对不对,全量级重量级的GC,所以一旦你的重量级的负GC出现了问题,你们内存就不够用,会出现内存溢出,对不对,诶就是这个意思啊,所以这个呢,应该是我们在学Java逊机的时候,应该学过类似的概念啊,所以新生代我们这里呢,给他拿过来一个红色啊,然后这边一个老年代我们的一个绿色,它涉及到我们的什么GC的问题。
03:35
所以啊,大家会发现我们的Java虚拟机对内存呢,进行了什么不同的分类,不同的管理,那么我们Spark如果想优化它的计算的话,它其实也得把我们的什么内存做一个分类。所以啊,我们现在就说一下我们的Spark,它里面的内存其实也分三大类啊,来咱们说一下,刚才说的呢,是我们Java虚拟机当中的内存,现在咱们要说什么呢?说我们的这个SSPA当中,它会根据我们程序的执行情况,它把它分成什么呢?咱们叫存储内存,叫存储内存啊,就是这样存储内存,然后呢,还有还有什么东西呢,拿过来啊,咱们拷贝,拷贝以后还有一个叫执行内存啊,咱们写上咱们叫做执行啊,咱们的内存还有一个呢,叫其他内存啊,咱们写上咱们叫做其他,嗯。
04:26
咱们这边写上啊,咱们叫其他内存这里呢,我们用一个颜色啊,比方说用一个红绿色,这个用一个红色来表示啊,这叫存储内存,这叫执行内存,这叫其他内存,分了这三大块儿,它有什么作用的,就是你前面比方说我们的占,我们的堆,我们的方法区,其实它们是有不同作用的,对吗?他们是有不同作用的,所以我们能够很准确的区分出他们来,这个我相信没有任何的问题吧,对不对?同学们,那好,那我现在这三块它又分什么作用呢?首先存储内存,这个存储内存里面存储的主要是我们缓存,就是RDD缓存的那些数据,这是我们需要缓存的,对不对。
05:07
还有呢,就是我们的一些广播变量,咱们也会存进去啊,咱们的广播变量的数据,所以呀,我们其实对内存的管理主要说的是exor,为什么呢?因为真正在计算的时候是excuor来做计算,而咱们那个driver啊,它其实有的时候内存也可能是需要去增大一些,也需要去管理,但相对来说没有ecute那么频繁,所以我们现在主要说的内存管理主要说的是我们的ecute,那么这个里面就可以有广播变量了,哎,同学们,广播变量你们还记得吗?那他可以把我们task的那些重复的数据给它独立出来,共享到ex里面去,对不对,所以它就会放在存储内存当中,就是这个意思啊好,那么我们回过头来说什么呢?我们的执行内存,这个执行内存什么意思呢?咱们叫execution,这个存储呢,叫storage,叫做存储,这个叫做execution,那么执行内存它的主要目的很简单,就是我们Le过程中啊,Le过程中我们的操作啊,Le的那个操作就会占用执行内存,就是它,嗯,好,还有一个叫其他内存,其他内存说的简单点就是我们,呃,运行过程当中一些系统自带的啊,系统一些包括RDD啊,那些原数据的信息啊,咱们叫原数据的信息,这就是一些对象的基本信息呢,就会放在这个其他内存当中,咱们叫other。
06:31
啊,这个叫other,这个叫execution,这个叫storage,分成三大块儿,那么你分成三大块儿的话,问题来了,你是均分吗?对吧,你是不是均分呢?不是啊,咱们不是,所以啊,这地方我们需要给大家搞明白,就是他们其实是有一个比例大小的,比例大小的,比方说咱们回过头来同学看啊来。呃,把这个呢,我们放过来啊,这个叫其他内存,这个其其实啊,它还包括一块,它为了防止内存溢出啊,其实它还额外有一块内存啊,这个额外的这块内存呢,其实我们称之为什么呢?咱们叫预留的啊,把这个去掉吧。
07:08
把这个拿过来啊,拿过来把这个放过来,放过来以后我们放到这边啊,咱们放到这边,诶放到这儿啊,放到这儿我们写上咱们叫预留,咱们叫预留的一块内存,这块内存是固定大小,叫300兆啊,它有个300兆的一个预留内存大小啊,就是这个东西好把它写上18,嗯,行了,那么我们这个记住啊,这个是我们总内存减去300兆之后的剩余的一个内存,那这个内存呢,它占40%。他占40%,那他占40%,那这两个那总共就占60%的呗,当然前提条件是把这个300兆去掉,对不对?好那这两个跟大家说一下,它占多少呢?它占60%的,50%,说白了就是30%啊,就30%,好那这个呢,也是30%啊,就是这样的,所以啊,就是我们总共的内存是把它们三个加在一块儿,再加300兆,就是我们可用的最大内存,就这个意思。
08:09
好,那这个光这么说肯定不行,我们给大家看一看啊,来,咱们回过头来到源码当中,咱们有个叫Spark inv,当我创建咱们的Spark environment环境的时候啊,它里面有个创建,咱们往下走,往下走如果是driver怎么怎么办,这个咱们不考虑啊,咱们往下往下往下当中你会发现其中啊,它里面有一个咱们找一下在哪,在这叫memory manager啊,咱们叫内存管理。咱们这儿有一个内存管理,这个内存管理呢,它这边有一个名称叫统一内存管理,所以啊,咱们这儿给他来写上一下啊,咱们写上叫统一啊,咱们叫统一,这个呢统一啊内存管理,如果咱们有的同学接触过早期版本的Spark的话,你会发现他那是有一个静态内存管理的,但是从10SPARK3.0开始,已经没有那个静态内存管理了,它就叫做统一内存管理,说白了就是动态内存管理啊,到底怎么个动态,咱们一会儿再说,咱们回过头来,同学们看,在这个里面我们点一下点点一下以后在这面它会怎么办?它会取得当前最大的memory,我们点一下,点点完以后它会先取得我当前的系统内存,然后呢,去把我预留的内存给它取到,我刚才说过了,预留内存我们点一下,点点完以后呢,我们看看啊,它这边是有配置的,咱们后退啊,咱们后退,后退以后我们来看看啊,这个地方点点完以后。
09:40
多少啊,300兆,那预留的是300兆啊,这是可以配置的啊,然后呢,我们接着呢,往下往下以后啊,往下看怎么了,它这边会用系统的内存减去我们的预留内存,这是我们可用的内存,然后呢,可用的内存呢,它这边会乘以一个我们的fraction啊,这个是多少呢?是我们的什么?哎,咱们点一下应该是我们的0.6。
10:04
它是我们的堆内存减去300兆之后,有一个比例叫0.6,这个0.6你回过头看什么,你回过头看我们刚才给大家画的图,其实指的就是他们,他们两个,他们两个合在一块儿是0.6啊,我们是60%的意思,好,我们这里呢,我们后退啊,后退,后退以后,记住它返回的就是可用内存再乘以0.6,所以后退啊,这是我们现在能够使用的最大的内存,然后这个内存它又怎么了呢?往下看,它这边有个什么东西啊,叫storage。就是存储的那块区域叫reason,它这块区域是多少呢?是我们60%,再干嘛乘以它,它是多少点一下多少0.5,所以60%的50%是不是就30%啊,所以咱们这个图画的没有问题啊,就是0.6乘以0.5,不就是0.3嘛,对不对,就是这个概念啊,好,我们再来后推啊,后退,后退以后,那么它这个是叫storage,是我们的0.5 0.3了之后,我们再点过去点,点完以后你会发现怎么了,它这边有一个叫max hit memory,就是我们那个0.6,再减去它的storage,就是那个零点什么0.3,然后呢,你点过去看一看,这啥东西叫execution,叫执行,就意味着它的这个storage memory是零点三百分之三十,他这其实也是30%对吗。
11:32
所以啊,对于我们内存管理来讲,它默认情况下就是初始状态,它应该是这种情况,所以啊,这个呢,我们看见了,在它里面呢,会有很多,比方说我们的什么这个存储啊,包括我们的执行啊,它里面都有,而且这里面还提到了一个概念,叫on hip和off hip,这个是什么意思呢?给大家解释一下,一个叫堆内,一个叫堆外内存。什么叫堆外内存呢?说白了就是我们系统所带的那个内存,我们Java虚拟机所管理的内存,我们称之为叫堆内内存,我们Java虚拟机管理之外的那个系统的内存,我们称之为叫堆外内存。那为什么会有堆外内存呢?是因为我们的Java虚拟机管理的内存,我想去控制它数数据或者说资源的释放。
12:19
你做不到,你让他释放我们的数据,释放我们的内存,你只能告诉他,但什么时候释放是不确定的,这样的话就不能够受到我们的控制,你只能通知而不能控制,这样的话就不灵活,你想要去加载数据,发现诶它没释放,那么内存就可能不够用,所以不是很方便,怎么办?我向咱们的。系统去申请一块儿我们的内存,我自己通过代码呢,对我的内存呢进行控制,比方说分配内存,释放内存,因为它跟家用虚拟机没有关系,是从操作系统借过来的,在这种情况下,你想怎么分配它就怎么分配,对不对?所以啊,这种我们的对外内存,其实我们用起来会更加的方便,但是相对来说也就不安全,对不对,为什么呀,因为它不是自动化的嘛,是需要你控制的,万一你不小心控制错了怎么办,对不对?所以啊,这些东西就会有风险啊,就会有风险,所以这个呢,我们了解一下,咱们了解一下好了,那我们接着呢,咱们往下来看吧,同学们啊,咱们接着呢就往下来看吧,这边有一个东西啊,咱们看叫0.5,什么意思呢?就是当你申请了我们的外内存的时候,叫off对外内存,它里面有一个叫对外的storage memory,这个是多少呢?是0.5,就是我们申请的外内存,它的50%是什么?是我们的存储用来做存储的,其实我们还有什么呢?另外百。
13:42
分之五十是什么呢?是我们的执行啊,是我们的执行啊,这个呢,我们其实就不用看这个地方了,咱们回头看课件,咱们课件当中其实啊,对这个其实是有说明的啊,同学们咱们往下看,往下看以后咱们就看这个地方吧,嗯。Spark1.6之后,它使用了我们统一内存管理,从Spark3.0之后只有统一管理,而没有早期的那个静态内存管理,大家看在早期的版本当中有一个叫静态内存管理,但是从SPA3.0之后没有了,它只剩下我们的这个叫做统一内存管理。
14:19
那么统一的内存管理当中啊,这里面会有一个图形,这个图形大家会发现跟我们刚才画的大同小异,咱们叫存储内存,这个叫执行内存,这个叫其他内存,而这个就叫预留内存。预留内存呢,是固定300兆对吧,然后这个是40%,它们两个是60%,然后各占50%,然后呢,我们这里面后面还有一个叫做堆外内存,堆外内存各占50%,诶就是这个意思。但是呢,这里提到了一个叫动态占用机制,这什么意思呢?大家会发现有个虚线,这个虚线所描述的概念就是说他们可以互相调用。什么叫互相调用啊,就是说诶,当你的内存不够用,而我的比较富裕的时候,我可以把我的内存呢,分配给你一点什么意思啊,就意味着我的执行的时候啊,只用这么多,那这块内存呢,就可以给你来用了,借给你来用,有借有还,再借不难嘛,对吧?所以这就叫动态的意思啊,动态意思,但是这个动态呀,有点问题,所以咱们往下,往下以后大家可以发现这是一个动态占用机制的一个图形演示,那么在这种情况下,大家可以看到来我们的storage叫做存储内存,这个叫做执行内存,那么存储内存和执行内存呢,互相什么呢?诶就是往里面填数据,填数据,当我们的内存都占满的时候,诶,谁也别借给谁,咱们一写磁盘就可以了,对不对,就是这样,但是在某些情况下,比方说我的执行内存诶比较富裕,我们的存储内存不够用了,我就准备占用你的内存来做我们的存储,可是当我占满之后,我的执行内存干嘛呢?我需要用内存了,怎么。
15:57
办,有借有还,再借不难,你把我们借过来的内存干嘛呢?给他还回去,还回去以后,那这块内存怎么办?就可能要淘汰,也可能要一些。
16:07
那按理说一减持方不就完了吗?对不对,但是它里面有个什么呢?淘汰的概念,所谓的淘汰的概念就意味着它其实在咱们早期学过一个叫存储级别,如果你的数据只能是memory only,就是存在内存当中,它就不能够一写磁盘,它只能淘汰,对不对?所以咱们当时给大家讲那个叫做持久化的时候,有个叫cash,咱们给大家讲过,在正常情况下数据都有可能丢失,对吗?所以RDD的血缘在我们cash的情况下是不能切断血的。诶,绝对不行,但是你的检查点可以切断学员,为什么?因为正常情况下你的数据都可能被丢失啊,都不是说什么内存不够用了,或者什么断电了,都跟这个没关系是吧,就跟正常情况都可能会丢失,绝对不允许我们切断血缘啊。好,我们再回过头来看这个,当我们的执行内存,大家看一下执行内存它多了,而我们的存储内存少用的话,那你不够用了,借过来用对不对?问题来了,你借过来用的时候,一旦存储内存发现,诶,我想要把它还回来。
17:15
我们的执行内存说不行,我凭能力借的,我凭什么要还,对不对?我就不想还。我凭什么要还呢?哎,这个时候他就不还完了,你不还这个存储内存怎么办,他就自己一写磁盘啊,或者说呢,淘汰数据。诶,老师,那这是为啥呀?为什么存储内存它不够用了,他借过去的就要还回去,而我们这个执行内存它不够用了,他借过来就不还呢?为什么?很简单,因为我们的这个存储的这个数据如果丢失了,你可以再走一遍我们的程序不会出现太大的问题,只不过性能会稍微差一些,它的延迟会更长一些,对不对?但是如果我们的执行内存的数据丢失了,那么大家想想你的执行的这个统计结果还对吗?
18:01
因为咱们的杀uffle会在我们的excution里面执行,那就意味着我们要做统计分析了,你连数据都没了,那你计的结果还正确吗?就不正确了,所以它是绝对不能在这儿被淘汰的,对不对?同学们这个能不能搞明白,你这个能搞明白的话,这个动态占用机制其实就够了,别的这些内存的这些东西我们暂时都不用考虑,就考虑基本的一个实现原理,以及它的这种存储规则,比方说哪些东西放哪块内存当中对不对?他们各自占在百分比大概是多少,他们的基本实现原理知道就够了。你要说把其他的底层的这个内存呢,比方说什么字节码该怎么存储啊,怎么放这些东西搞明白啊,就太偏向于底层了,这个我们暂时先不考虑好不好,同学们好,这个内存呢,咱们就先说到这里了啊同学们。
我来说两句