00:00
呃,从这个第三章开始呢,咱们就开始进入学习运行时数据区了,那我们来看一下这个图,前面的话呢,咱们把类加载器子系统呢做了一些讲解,咱们花了这个一章的内容来讲解它,那后续咱们在第二篇的时候,就是在我们的这个第二篇讲自检码和类的加载篇的时候呢,咱们在重点的把类加载器子系统剖析开,包括呢,自检码指令一点点呢带着大家去看,那现在呢,咱们重心呢就不在这儿了,先知道呢,类加载器里边的主要内容是什么,然后主要做什么事就可以了。那么当我们把这个类加载到内存当中,存放在方法区的时候,实际上这个时候已经开始使用我们运行时数据区了,啊,那这儿呢,接下来咱们来讲的就是这部分的重心啊,就该他登场了,那我们也看到运行师数据区呢,涉及到这个结构呢,就相对来讲多一些,所以整整个呢,咱们这个章节的话呢,涉及到呢,也相对来讲就多一些。从第三章开始一直到第。
01:01
11张。不能到第11章,第十章这些章节呢,都是咱们讲的运行时数据区的内容,或者呢,咱们其实应该把这个string table呢,也给它包含在里边啊,这呢是特意呢给它,诶作为一个单独的章节来讲了啊,是这些章节呢,都算我们的运行时数据区,所以内容呢是相对来讲多一些啊,那咱们这个第三章呢,呃,整体来做一个概述,顺便呢,我们再把这个现成的内容呢,再稍微的回顾一下啊,是这样子的。好,那我们就正式来开始说明啊,这个class文件呢,咱们已经说过了,是使用类加载器子系统呢进行的一个加载,经历过啊加载链接和初始化这样的三个过程,那加载完以后的话呢,我们就在内存当中的这个方法区呢,其实就保存了我们这个整个这个运行实类本身了,那接下来呢,我们需要呢,使用这个执行引擎呢,去做一个执行,那整个呢,执行引擎执行的过程当中,其实都要用到我们的运行时数据区,在这个区域里边都有哪些具体的信息呢?诶都分成哪些部分,大家各司其职,都做什么事呢?这就是咱们要讲解的中心啊,这个用完以后啊,后边又有个JC啊,是这样一个过程,诶我这儿呢又放了一张图。
02:17
诶,我觉得从这个角度来解释呢,稍微呃形象一点啊,这个大家呢,多少应该都见过啊,这个电视上也好,或者是这个图片也好啊,见过这个厨师这个做饭啊,尤其电视上有一些,呃,中央台你看的那个大大厨做饭的时候呢,这个台面呢,摆的东西是非常多的,对吧?嗯,那这儿呢,就类似于后边这个区域一样,包括上边的这些工具啊,嗯,把这些呢,咱们就可以理解成是咱们的一个,嗯,内存的运行时数据区。啊,内存的应用是数据区,就是在这个区域里边,你会发现在各自不同的位置呢,放了各种各样的东西啊,在这儿呢,是放的这些工具,呃,这儿呢放的这个台面有刀啊,各种各样的一些这个刀有时候都有很多很多把是吧,还有一些调料啊,专门的一些这个蔬菜的一些东西,这呢都可以列解成是我们的运行时数据区,那厨师呢,就类似于咱们的叫执行引擎,哎,那这个厨师呢,就开始使用运行时数据区,根据咱们的自解码指令,然后呢依次去执行,把它翻译成机器指令,然后咱们这个CPU呢去解释运行。
03:24
诶是这样一个过程,然后呢,做完饭以后呢,最后还得要清理这个台面嘛,就是把我们的内存呢,还要做一个回收啊,大体上呢,跟我们这个呢,是有点类似的啊,那下边呢,我们就来看一看具体的这个内容啊。嗯,那首先呢,我们来提到这个内存的一个概念啊,运行是数据区,我们主要呢,其实就是用的内存了,说内存呢,是非常重要的系统资源啊,不用多说了,它是硬盘跟CPU的一个中间仓库和桥梁啊,这个大家应该都清楚,承载着操作系统和应用程序的实时运行啊,这呢,如果画一个简单的表示的话呢,这如果是CPU,哎,这呢,哎,比如说是咱们的这个硬盘。
04:06
那是硬盘,包括呢,你也可以是这个网络也行。Net网络也行,那这个网络中的数据或者硬盘中的数据呢,我们要想能够被CPU这个运算,那他需要呢,先把这个数据呢,加载到咱们的这个内存层面,呃,Memory这个层面,然后呢,CPU读的数据呢,都来自于内存。啊,都来自于内存,或者说呢,或者说呢,这个CPU呢,直接交互的对象呢,就是我们的内存,所以呢,内存呢,相当于是充当着CPU和硬盘的桥梁,哎这样啊,那GM呢,内存布局规定了,Java呢,在运行的过程当中,内存的申请分配管理的相关的策略就是你不管要呃,因为咱们说的Java虚拟机呢,叫自动的,呃,这个对象分配机制啊,你自动的去分配内存,那你当然也要负责呢,对这个内存的这个垃圾呢,进行自动的回收,诶都是由我们虚拟机来做的。
05:01
进而呢,保证了我们虚拟机的一个高效稳定的运行,那不同的Java虚拟机啊,咱们在第一章当中也讲过了啊,有很多具体的落地的GVM的这些产品啊,那他们对内存的划分方式和管理机制呢,是存在一些差异的啊,有一些不同啊,典型的这个不同呢,其实就是针对于这个方法区,呃,有区别啊,像我们说的g rocket和G9,它是没有方法区的,呃,所以说这个图呢,主要针对的还是houseport,呃,Houseport来讲的啊。哎,就跟这个图是一样的啊,House。啊,那结合呢,GM这个规范啊,咱们来探讨一下这个经典的这个布局,诶这呢主要刚才说了是针对于houseport,那这个区域呢,我这儿留了几个箭头,它呢跟其他的结构呢,要进行一些交互,上面这个箭头就是我们已经讲过的啊,叫类加载器子系统。诶,这个我们可以给它补齐一下啊,这叫列加载器子系统,然后呢,我们内存中的这个数据呢,它不是要静静的放在这儿的,它需要呢使用啊叫执行引擎。
06:06
来进行一个解释执行啊,就是将我们的自解码指令呢,翻译成这个,呃,机器指令啊进行呢解释执行啊,这是执行引擎啊,它的一个工作,然后这块的话呢,那我们在这个官方的这个图里边呢,也会看到它这块呢,主要是涉及到了本地方法站当中使用的,诶叫本地方法接口,诶一呢哎对应的叫哎本地方法库。因为我们在这个或者叫Java语言在刚开始兴起的时候呢,其实正是C语言大行其道的这个时候啊,那我们Java呢,为了更好的能够融合C,诶,我们可以去调用C的一些相关库,那这呢相关的本地方法,你要调用的话呢,我们就有对应的这个站啊来进行分配,诶所以这呢,就涉及到本地方法库的一个调用的情况,哎,就是这样的一个图,哎这个图的话呢,咱们说过啊,需要大家会画。
07:00
就这张图完整的你要能够画出来,那慢慢来咱们讲,讲完哪的话你就会画哪,然后最后呢,整个一张图能够画下来就可以了,哎,最终呢,你要生成一张这样的图。甚至呢,你得比我这个划的还可以再复杂一些,呃,那就到位了啊就可以了,行呃,这是我们说的这个情况啊,然后说这个具体的这个内存划分是什么样的呢?我这放了一张图啊,这个图呢,实际上是从这个诶阿里出的这个手册里边拿到的一张图,诶跟咱们画的其实整体上是一样的啊。那将运行时数据区呢,我们分成这样的几个部分,嗯,有这个本地方法站啊,虚拟基站啊,这是两个站的区域啊,这两个站的区域呢,针对于虚拟站,这也是咱们这这个运营时数据区里边重点要讲的一个区域,它呃这个基本单位呢叫战帧,里边呢,又分为局部变量表或者叫本地变量表,操作数站,然后动态链接,还有呢,方法返回地址啊,还有一些附加信息啊等等啊是这样,我们先不展开来说哈,你会发现我在这儿呢放了很多的战争啊,因为呢,一个战争呢,我们这个对应的一个方法啊,这样。
08:12
然后呢,这个叫程序计数器啊,PC register啊,程序计数器或者我们叫PC寄存器啊都可以,然后这是堆取堆区的细节呢,又分成伊甸园区,幸存者零幸存者一区啊,整个呢,这都叫做这个新生代啊,或者叫年轻代啊都可以,然后这呢叫老年代啊,老年代O的区。然后呢,我们这儿呢,还有这个原空间啊,这一看呢,就肯定是JDK8以后的这个版本了,因为以前的话呢,我们叫做永久带了啊,这个我们把这个区域呢,其实整个呢,都可以称为叫非堆空间。啊,非堆空间叫Meta space原数据区,还有呢,是git编译后的一个,诶代码缓存区啊这样的,诶metapa里边啊有这个class类源信息常量池啊,其实呢,加载完内存中以后呢,叫运行式常量池了,方法的原信息等等,这个咱们后边呢都会展开说,其实呢,跟咱们诶画的这个图呢是一样的啊咱们就一部一部分一部分的来展开的去说啊是这样子的,行,那我们再回过来强调一下。
09:17
刚才呢,回顾一下咱们关于运行时数据区一共讲了几个部分呢。啊,应该是有五个部分,12345对吧,哎,五个部分,然后我画的这个图里边呢,你看有两种颜色,一种呢叫红色,一种叫灰色啊那这个红色的你看啊,说Java虚拟机中定义了若干种程序,运行期间会使用到运行时数据区,其中有一些呢,会随着虚拟机的启动而启动。随着虚拟机的退出而销毁,也也就是说呢,它跟虚拟机呢,呃,生命周期是一样的,那我们说一个虚拟机呢,它对应着一个进程,那就是它跟这个进程的生命周期呢是一样的。
10:01
啊,那进程里边呢,我们可以有线程,那么有一些结构的生命周期和线程呢,是一一对应的。啊,那线程呢,出生他就出生了,线程的结束它就结束了,所以生命周期呢,和线程是一致的。啊,线程进程的关系,大家这个都比较清楚了啊,一个进程当中我们可以有多个线程,那么回过来这里边的红色区域就是诶进程一个进程对应的一份儿,我们也可以理解成一个虚拟机实例对应的一份,而我们这个灰色的呢,就是一个线程一份。那问一下大家啊,比如我们一个啊程序当中,或者我们一个运行的程序叫做进程了,它里边呢,有五个线程。五个线程啊,那实际上是不是就意味着我们有五组程序,计数器,本地方法站,虚拟基站。对吧,哎,有五组这个,那这五组线程它共用方法区和对空间。
11:04
哎,这呢是大家首先需要明确的啊,哎,共用这个方法去和对空间,那我这儿呢,又画了一个图,就是一个线程呢,哎,他们仨,哎都各自拥有PC,就是PC寄存器啊,Virtual machine stack,虚拟驿站啊,Native method stack就本地方法站,哎一个线成一份,然后hip和method area他们是共用的。哎,就是这样一个图啊,是这个意思,那正因为呢,我们说这个,呃,这个叫什么呀?堆空间和方法区呢,是共用的啊,这里边会涉及到像以前我们讲线程的时候呢,涉及到这个线程的一个安全问题,大家都要用,那这个怎么保证它只有一份呢?比如我们在前面讲这个类的加载的时候呢,说这个。内存当中,比如说你这个线程和这个线程都要用这个类了,这个类只会加载一次对吧?哎,那这时候呢,保证它得是一个同步的操作啊,有一个这样的问题啊,那另外的问题呢,就是说我们这个比如说针对堆空间创建了多个对象,那这些对象的一些回收啊,呃,这个这个或者没有回收的怎么去给他接着一步步的去这个保存起来呀,是吧,有一个递升的一个机制哈,这呢都是我们重点要讲解的,诶这个在我们整个说GM优化当中,其实呢,针对于这个线程里边这几个结构呢,没有太多可优化的点啊,你像你这个虚拟机站啊,你就是一个站的一个结构。
12:27
只有呢,进站和出站这样两个操作啊,你不使用了就让它出站,使用的话呢,就给大家加载进去叫入站,所以呢,关于它的这个垃圾回收呢,其实我们就不谈了,正常的入站出站这个操作就行啊呃,没有垃圾回收,但是它照样也会出现这个溢出的问题啊,到时候再讲,那重点咱们进行优化啊,就是我们的堆空间。啊,然后也包括方法去。放弃主要就是放咱们这个类的信息,那从咱们这个频率上来说呢,简单来讲,95%我们的垃圾回收都可以理解为集中在堆取,那5%呢,是集中在我们这个叫呃方法区。
13:07
是这样子的啊,那方法区呢,在JDK8以后呢,又换成叫呃原空间了,那使用的是本地内存,那本地内存我们说还是比较大的,如果你没有进行过参数设置,那你这个方法区呢,一般情况下呢,就不会出现溢出了,因为本地内存一般都比较大,但万一要溢出了呢,那也会考虑它的回收问题,哎,所以但是这个频率出现的就很低了啊哎,重心在这儿。哎,重心在这啊行,这个清楚我们这里边所谓的共享,哎这儿呢,方法区我写成一个叫对外内存了,你要体会一下,对外内存的话呢,嗯,其实永久带源空间,大家就可以暂时理解为是方法区的一个落地的实现啊,这样呢就理解成是方法区就可以了,这个代码缓存的话呢,这个在不同的书里边,这个情况不一样啊,有的呢,把我们说的这个GI编译以后的这个代码缓存呢,认为就是独立于源空间啊,当然跟源空间肯定是独立的了啊呃,有的会认为他就是也算方法区的一部分啊,有的呢,认为他就应该独立出来的,这个我看,嗯,这个朱志明老师里边提的呢,是考虑到把它放进去了,然后呢,那阿里呢又把它拿出来了,这个我觉得嗯,没关系啊,没关系,不要去细抠这些细节了,你只需要知道呢,它是非堆空间,就是我们缓存的这个数据呢,不是在我们这个堆空间啊,也不用你在这里边考虑考虑这个垃圾回收就可以了啊。至于说呢,这叫员。
14:32
空间,我们把这个呢,整个叫成一个方法区,可不可以呢?也可以啊,也可以的。行,这是我们说的这个问题啊,然后下一个呢,咱们在前面也提到过这个事,这个runtime runtime就是我们说的一个运行时对象啊,诶这样一个类,诶我们打开这个API呢,看一下。行,哎这呢,就我们说这个类,哎这个类呢,咱们前面强调过哈,说一个GVM实例。
15:03
啊,一个GM实例呢,就对应着一个runtime的实例。啊,那这个runtime呢,它的一个对象就相当于咱们提到的这个,呃,运行时数据区。啊,整个这个运行值数据区呢,呃,对于一个虚拟机来讲,就只有这一份哈,那它就相当于是我们一个GM所定的runtime这样一个对象,你看在它的第一个介绍的第一句话呢,说的非常清楚,说每一个障碍应用程序都有当前这个class runtime的一个唯一的实例。啊,唯一实力,然后允许呢,我们应用程序呢,嗯。In which在这个应用程序运行当中呢,诶通过我们这个应用程序呢,去做一些交互操作,然后当前的这个runtime呢,它可以通过get runtime这样一个方法呢来进行获取,就是当前这个runtime类的对象啊,可以通过get runtime方法进行获取,诶是这样子的,它是一个单立的啊,因为在一个虚拟当中,这个它就只有一份啊,有个这样的一个对应关系啊就可以了。
16:06
行,这儿呢,我们是把整个这个运行时数据区来进行了一个整体的概述啊,大家呢,应该是心里有数啊,那后边的话呢,咱们就具体来展开讲,那这里边儿呢,有几个章节是很重要的啊,你猜其实也能猜出来啊,虚拟机站。方法区啊堆啊,这是我们讲运行时数据区里边的三大重点的章节啊,这个大家要心里有数就行啊。
我来说两句