java之jvm学习笔记三(Class文件检验器)

前面的学习我们知道了class文件被类装载器所装载,但是在装载class文件之前或之后,class文件实际上还需要被校验,这就是今天的学习主题,class文件校验器。

class文件 校验器,保证class文件内容有正确的内部结构,java虚拟机的class文件检验器在字节码执行之前对文件进行校验,而不是在执行中进行校验
class文件校验器要进行四趟独立的扫描来完成校验工作

class文件校验器分成四趟独立的扫描来完成校验。

第一趟

在装载字节序列的时候进行,这个是校验class文件的结构的合法性,比如你使用
windowns下的copy命令去合并一个.class文件和一个jpg文件的时候,在装载这个class文件的时候jvm会发现这个class文件被
删改过,文件的长度也不正确,而抛出异常!
所以这次校验是发生在二进制数据上,

第二趟

扫描发生在方法区中,主要对于,语义,词法和语法的分析,也就是检查这个类是否能够顺利的编译!

第三趟

字节码校验
在这一趟的校验中涉及两个比较不好理解的概念,第一个是字节码流,第二个是栈帧.
执行字节码时,一次执行操作码,java虚拟机内构成了执行线程,而每个线程会有自己的java栈就是我们说的栈帧。每一个方法都有一个栈帧。
如果学过汇编的人理解这两个概念会容易一点
字节码流=操作码+操作数,在这里可以看做汇编里的伪指令+操作数,因为这里的操作码实际上就是给jvm识别的“汇编伪指令”,而操作数的概念和汇编里的除了数据类型,并没有多大的差异
重点来看一下栈帧,栈帧其实也很好理解,栈帧里有局部变量栈操作数栈,这两块内存就是放数据的时机不同,操作数栈就是用来存放字节码指令执行的中间结果,结果或操作数,而局部变量区,就是用来存局部变量形参等,这个很好理解
这个字节码的校验过程校验的就是字节码流的合法过程,也就是校验操作数+操作码的合法性。

而java的class
文件编码我们之所以称之为字节码,是因为每调条操作指令都只占一个字节,除了两个例外情况,所有的操作码和他们的操作数按字节对齐,这使得字节流在传输的
时候跟小,更有优势,这两个例外是这样一些操作码,在操作码和他们的操作数之间会天上一至三个字节,以便操作数都按字节对齐。

下面是一个图,描述了栈帧的结构

第四趟

符号引用的校验
由于大部分jvm的实现都是延迟加载或者说动态链接的,延迟加载的意思就是,jvm装载某个类A时,如果A类里有引用其他的类B,虚拟机并不会把这个被引用B类也同时装载入内存,而是等到执行到的时候才去装载。
而这个被引用的B类在引用它的类A中的表现形式主要被登记在了符号表中,而第四趟的这个过程就是当需要用到被引用类B的时候,将被引用类B在引用类A的符号引用名改为内存里的直接引用
所以第四趟发生的时间是不可预料的,而且发生在方法区中。总个这个过程称之为动态连接
可以简单的划分为两步
1.查找被引用的类(有必要的话就加载它)
2.将符号引用替换为直接引用,例如一个指向类、字段或方法的指针,下次再需要用到被引用类的时候直接运用直接引用,不需要再去装载。

这个过程其实在ClassLoader类中的loadClass中就可以发现它的痕迹。我们先贴出loadClass这个方法实现,然后简要的做一下分析

[java] view plaincopy

  1. protected synchronized Class<?> loadClass(String name, boolean resolve)
  2. throws ClassNotFoundException
  3. {
  4. // First, check if the class has already been loaded
  5. Class c = findLoadedClass(name);
  6. if (c == null) {
  7. try {
  8. if (parent != null) {
  9. c = parent.loadClass(name, false);
  10. } else {
  11. c = findBootstrapClass0(name);
  12. }
  13. } catch (ClassNotFoundException e) {
  14. // If still not found, then invoke findClass in order
  15. // to find the class.
  16. c = findClass(name);
  17. }
  18. }
  19. if (resolve) {
  20. resolveClass(c);
  21. }
  22. return c;
  23. }

loadClass有两个参数,第一个参数是类的全限定名,第二个参数就是我们要说的重点,这个参数为true的时候表示,loadClass方法
会执行resolveClass的方法,这个方法就是将类中的符号引用替换为直接引用。最终调用的方法是一个本地方法 resolveClass0。

这里还有一点需要注意,Class.forName这个静态的方法我们也常用来加载class文件的字节码,那它和classLoader有什么区别?

区别就在于是否执行resolveClass这个方法,Class.forName总是承诺将符号连接进行连接和初始化,而loadClass没有这样的承诺。

总结:

第一趟扫描,在类被装载时进行,校验class文件的内部结构,保证能够被正常安全的编译
第二趟和第三趟在连接的过程中进行,这两趟基本上是语法校验,词法校验
第四趟是解析符号引用和直接引用时进行的,这次校验确认被引用的类,字段以及方法确实存在

时间: 2024-11-20 13:31:50

java之jvm学习笔记三(Class文件检验器)的相关文章

java之jvm学习笔记六-十二(实践写自己的安全管理器)(jar包的代码认证和签名) (实践对jar包的代码签名) (策略文件)(策略和保护域) (访问控制器) (访问控制器的栈校验机制) (jvm基本结构)

java之jvm学习笔记六(实践写自己的安全管理器) 安全管理器SecurityManager里设计的内容实在是非常的庞大,它的核心方法就是checkPerssiom这个方法里又调用 AccessController的checkPerssiom方法,访问控制器AccessController的栈检查机制又遍历整个 PerssiomCollection来判断具体拥有什么权限一旦发现栈中一个权限不允许的时候抛出异常否则简单的返回,这个过程实际上比我的描述要复杂 得多,这里我只是简单的一句带过,因为这

java之jvm学习笔记十三(jvm基本结构)

java之jvm学习笔记十三(jvm基本结构) 这一节,主要来学习jvm的基本结构,也就是概述.说是概述,内容很多,而且概念量也很大,不过关于概念方面,你不用担心,我完全有信心,让概念在你的脑子里变成图形,所以只要你有耐心,仔细,认真,并发挥你的想象力,这一章之后你会充满自信.当然,不是说看完本章,就对jvm了解了,jvm要学习的知识实在是非常的多.在你看完本节之后,后续我们还会来学jvm的细节,但是如果你在学习完本节的前提下去学习,再学习其他jvm的细节会事半功倍. 为了让你每一个知识点都有迹

java之jvm学习笔记五(实践写自己的类装载器)

java之jvm学习笔记五(实践写自己的类装载器) 课程源码:http://download.csdn.net/detail/yfqnihao/4866501 前面第三和第四节我们一直在强调一句话,类装载器和安全管理器是可以被动态扩展的,或者说,他们是可以由用户自己定制的,今天我们就是动手试试,怎么做这部分的实践,当然,在阅读本篇之前,至少要阅读过笔记三. 下面我们先来动态扩展一个类装载器,当然这只是一个比较小的demo,旨在让大家有个比较形象的概念. 第一步,首先定义自己的类装载器,从Clas

java jvm学习笔记三(class文件检验器)

欢迎装载请说明出处:http://blog.csdn.net/yfqnihao 前面的学习我们知道了class文件被类装载器所装载,但是在装载class文件之前或之后,class文件实际上还需要被校验,这就是今天的学习主题,class文件校验器. class文件 校验器,保证class文件内容有正确的内部结构,java虚拟机的class文件检验器在字节码执行之前对文件进行校验,而不是在执行中进行校验class文件校验器要进行四趟独立的扫描来完成校验工作 class文件校验器分成四趟独立的扫描来完

java之jvm学习笔记二(类装载器的体系结构)

java的class只在需要的时候才内转载入内存,并由java虚拟机的执行引擎来执行,而执行引擎从总的来说主要的执行方式分为四种, 第一种,一次性解释代码,也就是当字节码转载到内存后,每次需要都会重新的解析一次, 第二种,即时解析,也就是转载到内存的字节码会被解析成本地机器码,并缓存起来以提高重用性,但是比较耗内存, 第三种,自适应优化解析,即将java将使用最贫乏的代码编译成本地机器码,而使用不贫乏的则保持字节码不变,一个自适应的优化器可以使得java虚拟机在80%-90%的时间里执行优化过的

java之jvm学习笔记六(实践写自己的安全管理器)

安全管理器SecurityManager里设计的内容实在是非常的庞大,它的核心方法就是checkPerssiom这个方法里又调用AccessController的checkPerssiom方法,访问控制器AccessController的栈检查机制又遍历整个PerssiomCollection来判断具体拥有什么权限一旦发现栈中一个权限不允许的时候抛出异常否则简单的返回,这个过程实际上比我的描述要复杂得多,这里我只是简单的一句带过,因为这里涉及到很多比较后面的知识点. 下面来尝试一下写一个非常简单

java之jvm学习笔记四(安全管理器)

前面已经简述了java的安全模型的两个组成部分(类装载器,class文件校验器),接下来学习的是java安全模型的另外一个重要组成部分安全管理器. 安全管理器是一个单独的对象,在java虚拟机中,它在访问控制-对于外部资源的访问控制-起到中枢作用 如果光看概念可能并不能很好的理解,或者说比较抽象,下面是ClassLoader其中的一个构造函数,先简单的看看它在初始化ClassLoader之前会做一些什么操作 [java] view plaincopy protected ClassLoader(

JVM学习笔记三:JVM类加载机制

概述 Class文件最终需要加载到虚拟机中之后才能运行和使用.虚拟机把描述类的数据从Class文件加载到内存,并对数据进行校验.转换解析和初始化,最终形成可以被虚拟机直接使用的java类型,这就是虚拟机的类加载机制. 类加载过程 类从被加载到虚拟机内存中开始,到卸载出内存为止,它的整个生命周期包括:加载.验证.准备.解析.初始化.使用.卸载 7个阶段.其中验证.准备.解析部分统称为连接.七个阶段发生顺序如下: java中,类型的加载.连接和初始化过程都是在程序运行期间完成的.加载.验证.准备.初

Head First Python学习笔记(三):文件与异常

一.os模块 os.getcwd()获得当前工作目录 os.chdir("...")切换到特定目录(一般为数据文件所在目录) 二.处理运行时错误 两种截然不同的方法: 1.“不要让错误发生”:增加更多错误检查代码 2.“异常发生时再做处理”:再增加一层异常处理 总结:异常处理能够让你重点关注你的代码需要做什么,而不会掉进“增加更多错误检查代码”的漩涡. 三.函数和关键字 open(),close(),readline(),seek(offset,from_what),tell(),sp