每天进步一点点!
上一篇已经学习了加载阶段,这一篇我们了解一下验证的过程。
我们已经知道了,虚拟机加载的是Class字节码文件,我们也通过工具查看了文件中存储的是二进制流。
下面我们打开一个文件AppTest.class,我们在文件的开头随便加入一个字母“f”,如下图所示。
那么,我们通过命令行输入命令“java AppTest”,会出现什么结果呢?
虚拟机会报一个Class文件格式化错误,这是我们随便修改的一个,但是如果某些别有用心的人,恶意修改了class文件,为了避免对虚拟机造成伤害,虚拟机必须要在确保Class文件的内容符合虚拟机的要求规范。
验证部分主要包含下面四个阶段的验证:
1. 文件格式的验证:验证文件格式是否按照虚拟机的规范,也就是我们前面class文件结构中的内容,比如这是不是一个Class文件(看魔数,是否位CAFEBABE);Java版本是否符合当前虚拟机的范围(Java可以向下兼容,但是不能处理大于当前版本的程序)等等。
2. 元数据的验证:对Class文件中的元数据进行验证,是否存在不符合Java语义的元数据信息。
这里有的朋友可能会比较疑惑,什么是元数据呢?一般情况下,一个文件中都数据和元数据。数据指的是实际数据,而元数据(Metadata)是用来描述数据的数据。用过Java注解的朋友应该对元数据这种叫法并不陌生,对应的元注解,其实说的差不多都是一个意思。
举个例子:比如说我们定义了一个变量 int a = 1;可以理解成数据就是1,而元数据就是描述有一个字符串变量“a”,这个“a”的类型是int型的,它的值也是一个int型的1,这就是描述数据的数据,就是元数据。
3. 字节码的验证:通过数据流和控制流分析,来确定程序语义是否合法。
以数据来说,要保证类型转换是有效的;对于控制流程的代码,不能让指令跳转到其它方法的字节码指令上等……
4. 符号引用的验证:为了保证解析动作能正常完成,还需在虚拟机将符号引用转成直接引用的时候,判断其它要引用的类是否符合规定。比如,要引用的类是否能够被找到;引用的属性在对应类中是否存在,权限是否符合要求(private的是不能访问的)等。
最后再说一点,验证阶段不是必须的,如果代码运行已经稳定了之后,可以通过设置参数-Xverfy:none参数来关闭类验证,减少虚拟机的类加载时间,提高运行效率。
既然说到了验证,不知道朋友们有没有想到另一个问题,那就是混淆。
有很多时候,为了使我们的代码不被反编译出来,我们会对程序的关键部分进行加密处理,让文件不容易被反编译出来,混淆技术应用而生。
这里就简单的给朋友们介绍一个工具——ProGuard,想要使用的朋友可以自行搜索查看,这里需要注意的是,混淆工具并不是百分百好用,有些涉及到反射的类是不能使用混淆工具的,否则会出现问题。
喜欢文章或想一起学习的朋友可以关注我,给我点赞,我将会持续更新,有什么疑问或文中有不当之处请给我留言,真诚地希望能与大家一起交流探讨,学习进步。