zoukankan      html  css  js  c++  java
  • java之jvm学习笔记三(Class文件检验器)

     

    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这个方法实现,然后简要的做一下分析

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

  • 相关阅读:
    第一章:计算机网络参考模型
    阿里云ECS hadoop+spark+zookeeper+hive code-server 集群搭建
    IDEA SSM+MAVEN+JWT 图书管理系统
    IDEA SSM后端框架入门
    code-server Command ' ' not found
    code-server scala error: object apache is not a member of package org
    下面给出一个child-parent的表格,要求挖掘其中的父子辈关系,给出祖孙辈关系的表格。
    现在有多个输入文件,每个文件中的每行内容均为一个整数。要求读取所有文件中的整数,进行升序排序后,输出到一个新的文件中,输出的数据格式为每行两个整数,第一个整数为第二个整数的排序位次,第二个整数为原待排列的整数。
    对于两个输入文件,即文件A 和文件B ,请编写MapReduce程序,对两个文件进行合并排除其中重复的内容,得到一个新的输出文件C。
    现有以下关系型数据库中的表(见表4-20表4-21和表4-22),要求将具转换为适合Hbase存储的表并插入数据。
  • 原文地址:https://www.cnblogs.com/timssd/p/4790475.html
Copyright © 2011-2022 走看看