00:00
接着呀,咱们来看一下这个过程二,这个过程二呢,我们统称为呢,叫做链接阶段,那具体的话呢,又分成了三个环节,分别对应呢,叫做verification验证,Preparation准备和resolution,叫解析,那我们就分别呢来进行一个说明。那么这里边儿呢,会有一些细节,咱们主要大家关注的就是验证准备和解析当中主要做的事是什么,那这个呢,能够理解清楚就可以了,那首先呢,咱们来看一下这个验证。说呢,当类加载到这个系统之后呢,就开始链接操作,那验证呢,就是我们所谓的第一步,那验证这个呢,一看这个词大家就应该明白这个意思,对吧?我们在生活当中实际上也有各种验证的这个场景,比如说你坐火车的话呢,你得验证一下你的身份证,然后呢,验证一下跟你这个票呢是不是匹配的,那这这种包括呢,我们还得做这个,呃,这个这个比如说你的行李的一些检查呀等等是吧,那现在的话呢,我们在这个疫情期间的话呢,大家呢,进入商场啊,进入一些公共场所的时候呢,我们还都得要验证一下你这个个人的这个轨迹啊,看一下这个健康码对吧?呃,测一下体温,呃,这呢都属于这个验证,那咱们现在呢,把这个4000码文件呢,你想给它加载到内存中,想去使用,那我们呢,也得需要做这个验证。
01:15
那验证之前呢,你相应的就跟这个人一样,你得先过来吧,是吧,你得先进来,你进来以后呢,我开始验证,那进来的话呢,就是我们说的这个第一个环节叫做加载环节,你都加载了,那这时候咱们就开始做验证了,那这也是咱们一开始就要做的事儿,那验证都没做,你后边准备啊,解析啊,这些都别谈对吧,就跟说你这个人的体温那一下子就超了38度以上了,那个商场就别去了,是吧,什么什么买这个买那个不行,进不了对吧,不合法属于这个意思,所以说呢,我们这个验证阶段主要做的事呢,就是保证加载的这个字解码呢,是合法的,正确的,符合规范的,那就是这个意思,那具体的话呢,我们这儿呢,提到了有这样的四个环节。分别对应的叫做格式检查,语义检查,自检码验证和符号引用验证,那这样的四个环节,那具体这个每一个环节里边做的事儿呢,我右边呢,也给大家举了一些例子啊,首先呢,从整体上来看的话呢,我们说这个格式检查。
02:13
你看这个格式检查呢,里边包含了像魔术的检查版本的检查这个相应的这些指令,有些指令我们是有一个长度的,对吧,那个长度的检查像这些呢,呃叫格式检查,它呢跟咱们这个这个加载,你看跟我们这个加载呢,是同时来执行的。啊,这块大家注意一下,那也就是说呢,我们在这个加载阶段的时候呢,其实已经开始的这个格式验证的一个操作,但是呢,从这个嗯结构上来讲呢,我们把这个格式检查呢,归结到这个咱们的叫LIC肯N这个阶段里边啊,林肯这个阶段里边,这个大家稍微注意一下。好,那格式检查这里边儿就提到了,比如说我们说合法的一个自解码文件都应该是叫CABB开头的,像咱们刚才那会儿呢,是不是演示了一个像这个自解码文件,我们呢,是不是手动的给它改成1234了,发现呢,它不就报了一个相关的error嘛,那说明呢,你就不是一个合法的自解码文件,那像这块呢,就是对应的,咱们这呢,就叫格式验证码,那你格式验证都失败了,自然而然的也不会把这个二进制数据加载到咱们的方法区,对吧。
03:17
这个大家注意一下,包括呢提到的像这个叫版本检查,假设呢,咱们这个主版本加上这个副版本,那翻译过来呢,比如说咱们是在这个1.9的环境下你编译的,但现在你非要使用这个GRE是1.8的环境下去解释运行这个呢,我们说这个低版本,你是没办法解释这个解释运行这个高版本生成的这个最均码文件的,对吧。啊,因为里边可能有一些新特性,你这里边儿根本不具备,那这个呢,也是在我们的这个格式检查这个环节做的行,这就过了,然后咱们下边这三个环节它呢,哎,都是对应着咱们已经完成了这个类的加载之后,那就意味着呢,这三个环节都是在咱们这个,诶针对于方法区已经生成的叫类模板对象。
04:02
啊来实现的对吧,诶已经是有这个方法区了,好这个呢,大家稍微注意一下,那下边提到说,虽然说咱们这个链接阶段呢,它的验证呢,这个拖慢了咱们整个程序的一个执行速度,但是呢,它避免了这个字节码在运行的时候呢,还需要进行各种各样的检查。那这个很很好理解这个事儿对吧,那我们在门口呢查一次,然后里边呢,你再进各个小店的时候呢,咱就不用再查了,那其实呢,从整体上来看呢,反而呢是更节省这个时间。对应咱们中国的一句古话呢,叫磨刀不误砍柴工,看似呢磨刀慢了,但是我们后边砍起柴来呢,速度反而会更快,对吧?啊这个就不用多说了,然后下边呢,具体的咱们来看一下,比如说这个叫语义检查来说第二环节语义检查,顾名思义呢,就是看一下我们这个语法上是不是符合规范。啊,这些呢,大家应该都比较熟悉,我举了四个例子,比如说呢,我们所有这个类啊,是不是都有负类的存在,当然这里边我们是除了这个OB obli类之外,你看我们其他的类呢,这个你对应的这个负类呢,是不是存在的?
05:08
都要求有这个负类的存在,我们在这个词解码文件的角度的话呢,这个每一个类,这个咱们比如说随便找个像hello word这个类,我们查看它的这个,那词解码文件的时候呢,是我们就能看到它这个负类,那到底是谁对吧?那它的super class行,那以及的话呢,说是否一些被定义为final的这个方法或者是类呢,被重写或者继承了final修饰的方法呢?不能被重写修饰的类呢,不能被继承对吧?像死顿类这个咱都知道,那你要是还提供了这个子类了,那显然有语义上过不去了,说非抽象的类呢,是否实现了所有抽象方法或者接口中的方法,非抽象类呢,你能实例化,所以内部是不允许有抽象方法的,那你看你是不是实现了所有的抽象方法,对吧?说是否存在不兼容的方法。比如说呢,我们说这个在定义一个类多个这个方法的时候,假设都是重名的,叫M这个方法,我们要求这个方法这个行三列表一定是不一样的,对吧?那咱们前面讲测解码文件的时候,把这个呃,形态列表啊,反问类型啊,咱们这块呢,都归结成是不是叫这个描述符,对吧?从侧解码文件的角度来讲,说这个形态列表和法文类型这两块呢,只要有一个不一样,都算是不同的方法,但是咱们Java语法上来说,跟这个重载跟这个没关系,主要呢,咱们看这个形态列表你定不一样。
06:26
假设你要存在两个同名同参数列表的方法,这个时候我们的Java虚拟机就无从下手,不知道该调哪一个了啊,它会报一个叫WK的是吧?所以这个方法呢,重复了。那以及的话呢,我们说这个abstract抽象的话呢,这个方法就不能再是final的了,包括呢,抽象的方法不能是静态的,也不能是私有的,对吧,这个呢,都属于咱们叫语法语义层面的一个检查。哎,语义层面检查,哎,这个一说大家都能理解啊,然后再下边呢,就是比较复杂的一个环节,称为呢,叫字节码的一个校验。
07:00
自解码那验证一下我们这个方法里边的一个结构啊,当然了不管方法了,常量池里边一个结构,这个是不是合规的啊,就是最自解码这个细节层面的一个验证。那在字节码的这个执行过程当中,是否呢,会跳转到一条不存在的指令?那如果说存在这种不存在指定,那显然我们这样执行起来的话,这不就挂了嘛,是吧,那这是这个问题,那个函数的调用呢,是否传递了正确类型的这个参数。啊,你传的这个参数要求的类型,跟你真实传的这个类型,看看是不是一样的呢?啊,有这样的一个校验。那变量的这个赋值是不是给了正确的这个数据类型啊,变量赋值是不是给了正确的数据类型等等这些都算,比如说呢,传递的正确的参数,比如说咱们在这个操作书站中,我这是一个int类型的,我这呢也是一个int类型,咱这俩数据,然后接下来呢,我叫这个叫ii的。那就相当于我们取这个最顶端的这两个操作数站的这个数据做一个加和,对吧,这个是没问题的,那假如你这是个I,这是个L类型,你用一个I的就不合适了。
08:03
对吧,你要说应该用Li的是吧,那你这时候把这个I呢,是不是还得to l给转换一下啊,那或者说呢,你这用的是I的这块,你想把两个long呢,进行一个添加,呃进行一个加和这些也不对啊,那就相当于I的呢,你这块呢,传递了两个呃不准确的这个参数类型。包括呢,你这个变量赋值是不是给了正确的这个值啊,我现在这块呢,是一个int型的一个数据类型I,然后要求呢,你给我赋值,那你看你给我赋值,比如说你用的叫BI是吧,哎,那这块哦,那可以能赋,那比如说这块bif,你这块是一个bad类型,呃,别这样,比如说叫SI是吧。SF是那那这个范围内负负的32768到正的32767,你这块想给一个BAT类型去复值,那显然就不可以了,虽然说BAT底层呢,也是用的四个字结存,但是呢,你你用这个去付,那显然是不是超出人家BAT数据类型的一个范围了啊这呢就就不对对吧。那这里边呢,就提到了一个叫做战针的一个映射,叫stack map table啊这个呢,咱们前面没讲过,那咱们看一下啊,这个战映射帧啊,站映射帧就是在这个阶段这个实现的,它是用于检测在特定的自解码处起居微量表和操作站呢,是否有着正确的这个数据类型。
09:17
啊,就是这个他就是主要做这个的啊,但遗憾的是呢,100%准确的判断一个一段这个磁线码是否可以被安全有效的执行呢,是无法实现的,因此呢,该过程只尽可能的去检查出是不是有问题啊,其实这个过程通过了,那比如说这个过程无法通过,那肯定是加载不了,说明这个类型呢就不对,对吧,但是呢,我们这个过程通过的话呢,也不能说明这一个类呢就完全没问题。就是它只是我们校验的其中的一个环节是吧,这个出事了肯定是不行,当然呢,这个行了也不一定整个就行,就是这个道理,那这个站映射针的这个操作呢,咱们也可以给大家举个例子,你像咱们前面写的叫if Switch got got goto test是吧,这个里边嗯,咱们把这个呢,嗯,啊也不用重新编译,直接咱们看一下它这个结构,这个结构里边呢,像这个方法中,比如这个COMPARE1。
10:08
COMP1对吧,然后我们打开这个code,大家你会发现呢,它里边呢,有三个属性,Line number table local variableb table,这咱们前面都讲过了,还有一个呢,就是战映热,真的这叫sta map的table,你看这个结构里边啊,涉及到这个integer,那你看一下我们对应的这个代码的话呢,咱们这块呢,是不是得定义两个int型的这个数据是吧?嗯,你像这里边提到了,比如你这个12的这个位置,嗯,咱们这里边12,那BI的一个20。这个是一个BI push是20负128的正月二期范围,这个20呢,你看是不是正确的付给了我们操作数占里边的20。这个BI push不是放在操作入站当中嘛,对吧,你这个数据是不是正确的赋给了我们局部变量表里边这个in特型的变量I呢?啊,有这样的一个校验。啊,有这样一个校验,这个校验环节呢,就是咱们所说的叫资金码验证这样的一个环节。
11:00
啊,就是在这儿就直页码验证啊,好,这个前三次的这个检查当中,我们已经排除了,就是文件格式啊,这就是属于我们第一个环节格式验证的环节,嗯,排除了这个事儿,排除了这个语义的事儿,排除我们直解码的不正确性啊就这三个环节做的事儿,但是呢,依然不能确保类是没有问题的,哎,我们下边还有一个呢,叫做符号引用的一个验证。这个符号引用的主要是咱们下边这块。啊,主要是咱们这个下边这块,像解析这个环节,我们要把这个符号引用啊,是不是要转换成对应的这个直接引用,对吧,这是他主要做的事儿,所以我们这个符号引用的验证啊,这个环节呢,也主要呢,是在这个引用环节,呃,解析的这个环节的时候呢,咱们才会去执行。啊才会去执行,那看他主要做什么事啊,咱们要校验一下你这个符号引用对应的那个结构呢,是不是存在啊。你看这里边写的class文件,在其常量池中会通过字符串记录自己将要使用的其他类或者方法,因此呢,在验证阶段,虚拟机就会检查这一类或者方法呢,是不是确定是存在的。
12:05
啊,这个如果说呢,咱们要使用的一个类呢,在系统中呢,这个无法找到,这个我们就会报这个no class DeFine error,方法找不到就会报这样的一个no such method的一个error。大家注意这个问题,那举个例子的话呢,你比如说咱们在这个常量池当中,比如说这个常量池当中,嗯,我们找一下,比如这个类型的一个infer信息,我们这儿呢,是不是要调用一下这个class,一个infer信息,它呢又指向了一个我们的这个纯的一个字串啊,是这个86这。这个字串呢,就是Java long下的这个object,那我们要判断一下你当前的这个对应的这个信息啊,是不是真正的是存在的,那我们是不是真的有这样的一个类型存在,那包括呢,像这个浪类型的是吧,那这呢是我们都是转化成对应的这个基本数据类型对应的这个情况了。方案引用,方案引用呢,你看你这块呢,是哪个类中的一个方法,这个类在不在呢?能不能找到这个类呢?哎,这个呢,都是对应我们叫符号引用的这个相关的验证,那你这里边儿呢,使用这个方法,那是不是这样的一个方法对吧?那这个方法呢,我们能不能在这个类中就找到这个描述符的方法呢?
13:13
啊,包括你还得指定相应的这个name是吧内呢叫init,然后这是它的方法描述符,能不能找到具体的这个名字,这个描述符的这个方法呢,诶这个呢,都是属于我们叫符号验证啊赢的这个验证这个环节。啊,这个大家注意一下,那整个呢,我们这个要是验证都通过了,哎,我们下边呢,就会涉及到针对于其中的一些静态的属性一个赋值的环节啊,这就咱们下边这个事儿。
我来说两句