00:00
上一节啊,咱们把这个方法区当中这个主要结构呢说了一下,那其中有一个内容啊,咱们是给过掉了啊,我们再回来看一下这个结构,在这里边有一个非常重要的结构呢,叫做运行时常量池啊,那么这一节呢,咱们把这个运行时常量池呢,给大家呢做一个说明,这呢也是我们说方法集当中非常重要的一个结构。诶,非常重要的一个结构,好,我们来看。嗯,一提到方法区啊,我们说方法区里边有一个重要的结构叫做运行时常量值。那么在这个自解码文件当中,咱们有一个结构叫做常量尺,叫做常量尺,咱们呢,那会儿看的这个扎P反编以后得到这个文件当中呢,我们能看到这个结构,这呢就叫constant po是吧,就是这个常量值,或者呢,大家也可以通过咱们当前就那会看到这个程序,咱们做一个这个你看接lab啊,这样的一个查看,也能够看到这样一个结构,这呢就叫做常量池啊,这里边这个信息量是非常大的,或者呢也可以看作是咱们这个自建版文件当中信息量最大的一个。
01:01
你看从我们这个第12行啊,一直呢,延伸到我们这个第96行是吧?哎,这个信息量是比较大的,好,那这块我们来说明一下这个,嗯,我们现在呢,想了解的是运行时常量池,那我们说呢,要想大家呢去了解或者弄清楚这个运行时常量池,咱们呢,就需要弄清楚这个叫常量池是吧,有点绕,为什么呀。咱们刚才那会儿上一节带着大家看的时候呢,咱们看的都是自解码文件是吧,相当于是反编以后的这个自解码文件了,这个自解码文件呢,咱们看到的这些信息包括呢,叫预系信息也好,方法信息也好,还有这个叫类型信息也好,这些信息呢,通过我们这叫类的加载器,咱们刚才那会儿带大家看的这个信息,通过这个类的加载器啊,就把我们这个自解码文件中刚才说的这些信息呢,加载到这个方法区里了。诶,加在这个方法区里边,那么我们这个次解码文件当中的常量池,这个常量池把它呢,加载到咱们这个方法去以后,我们对应的这个结构啊,就称为叫运行时常量池。
02:05
就是在运行的时候呢,这个常量池的这个信息,我们称为呢叫运行时常量池,所以呢,咱们要想弄清楚这个运行时常量池,实际上呢,我们主要呢,把这个常量池是不是要弄清楚。诶,那下边呢,咱们主要来关注一下这个叫常量池,那这个常量池呢,属于咱们说这个自解码文件的一部分,那要想呢,弄清楚它,咱们就要会看这个字节码文件啊,就要会看这个磁解码文件,这个磁解码文件这个结构呢,长什么样子,这个咱们也可以看一下这个Java虚拟机的这个规范里边写的。诶八的这个规范,我们主要呢来看这个第四章啊,实际上呢,这一章呢,咱们现在呢,不是作为一个重点来讲的,咱只是呢介绍一下啊,是为了讲这个内存结构呢,咱们来介绍它的,呃,这个信息呢,咱们讲中篇呢,重点呢带大家来去分析这个最起码文件啊,就从这个角度来分析什么角度啊,咱们把这个这个我都关一下,这呢是咱们刚才诶这个桌面上没有啊,我把咱们当前的这个生成的一个自源码文件随便呢打开一个。
03:06
比如说就咱们刚才写的这个程序,CTRLC一下,这呢就是我们说的这个自解码文件,这个自解码文件呢,我们到这个中篇的时候呢,就是来一点点来讲解一下,说这个自己版文件,我们该怎么去分析它,或者呢直接把它一拖,咱就从这个角度来看,这呢是一个16进制是吧,一点点来看,一直呢把它翻译完看看呢,能不能诶这个大家看得懂啊,这呢其实是一个硬货是吧,以后呢,估计大家也不会深入到这个资金版文件这样的一个维度上呢去看了。这个呢是一个最原始的一个情况,行,那咱们呢,现在是讲这个上片主要呢是内存和垃圾回收,所以咱们就不细节的去讲这个事儿,但是为了呢,能够讲清楚我们这个内存结构啊,咱们诶稍微来说一说这个这个常量池的问题,那此时呢,咱们用的是这个JP这个指令啊来进行查看的,或者呢,直接我们在这看也行。好,那么整体上先来说一下咱们这个class文件的一个结构啊,它分成了这样的一些部分,那这些部分当中,其中呢,有一部分呢,是来介绍我们这个常量值的。
04:07
嗯,Constant pro count,以及呢,我们具体这个常量值信息主要呢,就是在这儿,前面的话呢,还有这个魔术最小版本,最大版本等等这个信息呢,也都包含着了,嗯,那其实呢,就类似于咱们在这儿来查看的时候呢,大家来看一下上面的情况。对吧,诶这块呢,就是我们最小版本,这呢就是个最大版本,这呢,前面省略的那个叫cafe baby啊,这叫魔术啊,就是专门来识别咱们这个文件呢,是不是一个合法的自解码文件,有一个这个标识叫cafe baby啊,这咱们后边再讲,那重点呢,咱们现在要讲的这叫呃,这个常量池是吧?那你也可以在这里边呢,往下翻去来看咱们这个常量池,我这呢就不做介绍了啊行,真的是这个问题。啊,这呢是我们把这个资金码文件呢,给它做一个区分啊,里边呢是一部分的一部分的这个结构,咱们关注的是这个常量池的这个数据,咱们通过账IP呢给他做了一个反编译,诶我们这儿呢,就能够比较清晰的看到它这个常量池的这个信息了,啊就这样子,好,那我们来看一下这个常量池这个描述啊,说一个有效的资金码文件中,除了包含类的版本信息、字段、方法、接口等描述之外呢,还包含了一项信息,那就是常量池表包含了各种字面量和对类型与方法的符号引用。
05:24
诶,很多人看这个信息呢,就看不太明白了,这个自变量,举个例子,像咱们刚才写的这个代码当中,咱们写的这个代码当中,比如说像这个数据十啊,像我们这个字符串啊,包括像这个字符串啊,这都算到一些叫字面量的信息。自变量是吧,这个大家应该学分字符串的时候应该知道这个概念,这都算一些自变量的信息,以及呢,我们在这个过程当中,像你使用的这个system啊,Out out对应的是print stream这样的一个类型的对吧,等等啊这样的一些类型,包括呢,我们这也是一个类型,这个副类也是个类型,像这些类型的话呢,它都会对应着有一个符号。
06:03
啊,都会对应的有个符号,那么这些符号的信息啊,叫符号引用的信息,包括自变量的信息,咱们呢,都给它存储在这个常量池当中。这就是他这个常量池的一个,这个里边放的东西啊,那下一个问题紧接着就问咱们为什么需要提供一个常量池呢。对吧,诶我我要这个常乐石到底干什么呢?有什么用呢?啊,我这儿呢,诶非常好的给大家解释了这个问题。大家看啊,我这儿呢,放了一个程序,这个程序呢,一看非常简单,就只有个方法。嗯,就这个方法,那这个嗯,代码我是放在这个文件里了,这个文件的话呢,是194字节。啊,这个文件呢,其实是非常小的,好,那虽然说我们这个文件很小,但是呢,大家你你会发现啊,我这个里边实际上用到的东西啊也不少。啊,都有什么东西啊,比如说你当前simple class这是一个类,然后呢,你是不是有负类啊,哎,负类呢,就是咱们object类类对吧?然后呢,你这个方法的话呢,是用的system system是不是也是我们要用的类,然后al呢,是一个print stream类型的,那print stream呢,也需要用杨咱们这个程序呢,要想给它加载到内存当中,你想想你这个类的副类是不是要加载,你要加载system也要加载,Print stream也要加载,那如果说咱们,呃把这个刚才需要的这些类,像这个string里边这个结构是不是还挺多的啊,那string里边呢,还应用到string相关的一些东西,那就更多了,那这几个类包括呢,我们当前自己这个类,如果呢,你要是都给它放到这个文件中的话呀,这个文件呢,肯定不止194字节了。
07:41
毫无疑问不止这个对吧,包括呢,我们这个方法呢,如果我要写很多的话呢,有的这个字符串,比如说都是这个hello的。啊,那这时候我们也没有必要呢,是不是给他生明多个呀,那此时呢,咱们就把你要用到的这些基本的信息,咱们作为一个符号呢去出现,这个就是一个符号引用啊,符号引用就是在咱们这个叫常量池当中呈现出来,都是一些符号引用,当你真正要是运行执行的时候呢,咱们这个呃,System就转换为转换为真正的咱们要加载的这个system这个类的那个直接引用了,就是在你这个方法区里边真正的那个它所在的这个位置。
08:20
啊,就说白了就是你这样只是一个,就好比是咱们这个符号引用呢,就有点像咱们看的那个,呃,画上的那个美女一样,对吧?哎,你看到那个画上的那个美女,或者你电视中看到那明星一样,当你真正的要去执行的时候呢,就有点像这个画上的美女呢,就走出来了,就是实打实的能够看得见摸得着的,当然呢,你摸一下可能有点耍流氓是吧,哎,能看得见的啊,实打实的一个三维的一个人,哎,或者呢,你看到的那个明星一样啊,就是真实的我们那个system那个类了,现在呢,都还是一些符号,那么把这些符号呢,咱们就都放在这个资金码文件当中啊,这个常量池里边了是吧?哎,就是方便我们去引用,而且呢,让我们这个文件呢,结构呢,就非常小。
09:02
应该非常小是吧,OK,那咱们呢,给大家来举个例子吧,这个干说的话呢,可能有点这个空洞是吧?好,那咱们就以刚才说的这个程序为例,不管是大家看这个T这个文件也好,还是我们这个接class lab来看也好,其实都可以啊,行,咱们举个例子。举个例子的话呢,咱们比如说就说一下咱们讲的一个下边的方法吧,比如说这个test的一,咱就比如说这个方法TEST1,这个方法呢,看似呢也是比较简单的,就这样一个结构是吧,那我们看一下这个方法,这个方法的话呢,看一下它这个code code呢对应的就是我们这样的一些操作符。啊,这样一些操作符,或者我们也承认具体的一个叫注意服也行,那具体的这每一个,比如叫BI push是什么意思啊,这个也是咱们到这个中篇的时候再给大家讲,如果呢,你现在感兴趣呢,咱们这个杰克拉在杰克拉lab呢,跟咱们Oracle官网呢,是做了一个这叫什么?呃链接啊,直接呢,大家去点击的时候呢,就能看到这个BF push是什么意思,这里边的每一个呢,都可以点击啊,非常的方便,都是一个超链接,行,那这块呢,我这样啊。
10:07
我把这个T1里边的这个各个注意这个这个操作符呢,咱们把它呢盯一下我放在这。嗯,凡是呢,大家你看到咱们在这些结构当中用到了这个叫井几井几井几的是吧,那这里边是不是都会有一些这个相关的一些结构啊。啊,这个经济,包括像这里边儿的这个,凡是带这个井这个结构的,其实使用的呢,就都是咱们上面这个常量池里的,比如这个叫井15,其实就是我们这个这个结构。啊,你看我现在看的是我们这是哪个方法啊,刚才我是不是看到这个叫呃CL in是吧,这个code里边呢,用的这个叫景15,哎,这个LDC呢,就是从这个常量池里边加载的意思啊,这个井15你在这块呢去找。哎,景15在这儿啊,这呢就是我们一个字符串的一个信息,这叫自变量,哎,它记录了就是这个信息,那这个信息呢,有可能在好几个位置都要用,我们呢,就把它在这儿做一个呈现。
11:04
啊,就这个意思,那咱们以这个为例吧,这里边呢,诶我们看这个叫景三,这个景三的话呢,你就直接在这里边去找这个景三,这个景三这块呢,又调用了这个井54和井55红色,这就别看了,红色的其实就是井54,井55的具体信息了,井54找一下。减54啊就在这这呢是一个class的一个信息啊,这是一个system的意思。嗯,西特米斯,然后仅54点,仅55呢,就是它啊这呢是我们调了这个outt,这个outt,呃这个调这个仅55呢,又调了一个74和75,这个out呢是74这个信息了啊这是相当于是我们用这个system,呃去调了一下这个55,这个55里边呢,又用了74和75,那咱们还得再找一下这个74。那74好是个alt啊,然后75呢啊,75呢是指明咱们这个alt呢,它的一个类型啊,是一个营类型,L呢表示叫营类型了,Print stream这个类型,这呢就是把这个景三这个呢就算是说清楚了,然后这个new用呢,哎这呢叫景四。
12:11
景四在这儿呢啊,这个井四里边呢,又用到了我们这个角井56,来找一下这个井56。在这这呢,其实是一个自变量的信息哦,它那是一个字符串了,叫string builder。叫。那这个完事了是吧,然后dump的话,它是对这个操作数占顶的进行一个复制的啊,这个咱们先不多说,然后再往下呢,叫special,这个呢叫井五井五找一下。那井五这块呢,又掉了井四啊,这个井52,哎,井四啊,井四呢,就是这个四零点。就是它了,哎,它其实就相当于是井四里边又叫这个井56了。56呢,就是咱们刚才看这个信息,咱们刚才实际上掉过这个信息是吧,然后呢,再回过来,哎呀,刚才是说的有点晕了啊,井五里边咱们掉了一个景四,景四已经看过了,然后井52找这个52。
13:08
减52呢,这里边儿指明的是又掉了25,掉26,那你再去找二十五二十六这个就一步步往下走就行哈,这个目前呢,带大家看,其实主要一个目的是什么呀?就是咱们这个减五,你看里边呢,各个去调其他的一个相关结构,主要目的呢,就是我们这块呢,是要创建一个string builder的一个对象。诶,我们在这个test里边造这个Li的对象是干什么呢?其实主要呢是在这,咱这其实相当于是个字符串,这个拼接操作,这个拼接操作呢,咱们这个后边呢,会讲这个字符串的时候说到啊,那这个操作的话呢,咱们底层呢,会去new一个string builder啊主要呢,就是在这儿能够看到的。那拟完以后的话呢,我们呃,跟这个连接的这个操作呢,是通过这个end来实现的。行,那其实呢,通过咱们刚才这个景三景四景五这块呢来演示,大家会发现呢,是不是整个呢,我们要使用的这里边的各种指令这儿呢,我还仅仅是以这个T1这个方法来说的,那其实呢,这些方法都一样啊,那每一个方法当中,它用的每一个具体的一些操作呢,是不是都是直接间接的体现了对我们长卵池中的相关一些结构的一些调用啊。
14:14
对吧?哎,那大家呢,我们举一个生活中的例子,你看你能不能这样去理解哈,就好比是什么呀,咱们在一个成语当中啊,这里边写了丰富的这个代码啊,丰富的代码,比如说像字串呢,就在好几个结构中都有去使用,那咱们这个自这个这个常量池啊,就类似于呃,这个咱们说呢,这个大家炒菜的时候呢,一些基本的一些原料。炒菜的时候的基本的一些原料,然后这个代码呢,就好比是你炒的一个一个的菜,每一个方法呢,就是一道菜,那你这些菜的话呢,是不是都有可能每一道菜,这道菜呢,要用一些这个香油和这个醋啊,这个菜的话呢,要用一些这个醋和这个酱油是吧?呃,还有一些配料的,比如说大蒜,那么这些基本的原材料啊,就好比是我们这个叫常量池。那就好比是他。
15:01
啊,那同时呢,我们可以再举个例子,就好比是这个画画一样,咱们知道学过这个前面同学呢,都知道这叫RDB是吧。那这个red green和blue啊,就是这三原色呢,就构成我们丰富的这个这个万千世界各种各样的颜色,那我们就没有必要呢,是不是这个每一种颜色呢,咱们都给备一个这个颜色值,像画家的话呢,他买的话呢,他也没必要说把这里边的,呃,这个颜料呢,所有的都买齐是吧,那可能会很贵,当然也有可能就没有你想要的,那那个颜色你只需要呢,有这个件红色绿色和蓝色的时候呢,咱们可以使用这个三颜色呢,调配出来咱们想要的这个1600多万种这个颜色。对吧,诶每一个呢,是不是256,然后呢,256乘256乘256就是1600万次是吧?行那就这个意思啊,就是这呢,就相当于是咱们这个原材料一样,咱们各种各样的这个行为呢,是实际上都是从这个原材料里边呢一点点呢去挑选出来的,这里边存放的像这个诶UTF8这呢,就是我们所谓的一些粘长胶子变量是吧?那还有呢,除了UTF这个八之外呢,还有一些像属性引用,方法引用啊类的引用信息等等啊这呢就是咱们这个常量池。
16:11
咱们用的这个相关的这些具体的这个代码执行的操作啊,这个细节你看都是从这个常量池里边去调的。哎,这个大家注意啊,行,那不知道呢,通过我刚才这个解释呢,大家能不能对我们这个常量池它的一个作用呢,大家有一个更深刻的理解,就是我们为什么要有它啊。行,那么这里边有什么信息,刚才也说到了,有这个数值,有字符串的值,也有类引用,字段引用,方法引用对吧?诶刚才呢,咱们在这呢,不都已经看到了吗?哎,这就是字符串的,这就是方法引用,这呢叫类引用啊,哎,这就是类嘛,类型信息,这呢就我们说的叫这个属性引用啊,这个class的话呢,可能是个类,它呢也可能是一个想接口是吧?哎,接口也有可能。咱们这里边儿会有接口吧,啊这这是个接口是吧,诶这个呢叫类型信息啊,它也不一定就是个类。
17:00
行,诶这呢,咱们就说清楚这个常量池里边呢,它到底是呃放什么以及呢,它有什么作用,嗯诶下边呢,这也是个举例啊,咱们呢,通过刚才的这个代码已经也给大家举例子了,最后呢,有这样的一段话啊,看大家能不能去体会啊,能体会的话呢,说明你是能理解的,说常量池啊,咱们就可以看到是一张表,那虚拟指令呢,根据这张表,这个常量表找到呢,要执行的这个类名。啊,你的这个方法名,这个参数类型,自变量等信息,哎,像这些信息呢,咱们都存放在咱这个常量池当中。啊,像咱们看到的这个信息那会儿呢,是不是也见到了,哎,这个信息呢,其实也是在我们这里边以一个自变量的方式呢呈现的。哎,这可能一下子找不到。哎,但是很显然呢,是就在这里边的,那包括呢,你看我们这个Z是吧,它也是一个这个,哎,自变量方式呢,给它存储的。OK啊,这样就行了,这呢就是咱们这个是不是叫方法名,刚才这块呢,是不是叫变量名,对吧?哎,能理解我们说的这个问题,行这呢大家先要对这个常量池呢有一个理解,然后呢你才能够再去理解,这叫运行时常量池。
我来说两句