《java虚拟机》----虚拟机字节码执行引擎

No1:

物理机的执行引擎是直接建立在处理器、硬件、指令集合操作系统层面上的,而虚拟机的执行引擎则是由自己实现的,因此可以自行制定指令集与执行引擎的结构体系,并且能够执行那些不被硬件直接支持的指令集格式。

No2:

执行引擎在执行java代码的时候可能会有解释执行(通过解释器执行)和编译执行(通过即时编译器产生本地代码执行)两种选择。

No3:

栈帧(Stack Frame)是用于支持虚拟机进行方法调用和方法执行的数据结构,它时虚拟机运行时数据区中的虚拟机栈的栈元素。栈帧存储了方法的局部变量表、操作数栈、动态连接和方法返回地址等信息。每一个方法从调用开始至执行完成的过程,都对应着一个栈帧在虚拟机里面从入栈到出栈的过程。

No4:

对于执行引擎来说,在活动线程中,只有位于栈顶的栈帧才是有效的,称为当前栈帧,与这个栈帧相关联的方法称为当前方法。

No5:

局部变量表是一组变量值存储空间,用于存放方法参数和方法内部定义的局部变量。

局部变量表的容量以变量槽(Slot)为最大单位,每个Slot都应该能存放一个boolean、byte、char、short、int、float、reference或returnAddress类型的数据。

reference类型表示对一个对象实例的引用,虚拟机能通过这个引用做到两点

1.从此引用中直接或间接地查找到对象在java堆中的数据存放的起始地址索引

2.此引用中直接或间接地查找到对象所属数据类型在方法区中的存储的类型信息

No6:

局部变量表建立在线程的堆栈上,是线程私有的数据,无论读写两个连续的Slot是否为原子操作,都不会引起数据安全问题。

No7:

虚拟机通过索引定位的方式使用局部变量表,索引值范围从0开始至局部变量表最大的Slot数量。

No8:

对象原本所占用的Slot还没有被其他变量所复用,所以GC Root一部分的局部变量表仍然保持着对它的关联。如果手动将其设置为null值(把变量对应的局部变量表Slot清空)System.gc()才会回收掉内存。

No9:

类变量有两次赋初始值的过程,一次在准备阶段,赋予系统初始值;另外一次在初始化阶段,赋予程序员定义的初始值。

局部变量如果定义了但没有赋初始值是不能使用的。

No10:

整数加法的字节码指令iadd在运行的时候操作数栈中最接近栈顶的两个元素已经存入了两个int型的数值,当执行这个指令时,会将这两个int值出栈并相加,然后将相加的结果入栈。

No11:

操作栈是一个后进先出栈。

No12:

每个栈帧都包含一个指向运行时常量池中该栈帧所属方法的引用,持有这个引用是为了支持方法调用过程中的动态连接(Dynamic Linking)。Class文件的常量池中存有大量的符号引用,字节码中的方法调用指令就以常量池中指向方法的符号引用作为参数。这些符号引用一部分会在类加载器阶段或者第一次使用的时候就转化为直接引用,这种转化称为静态解析。另外一部分将在每一次运行期间转化为直接引用,这部分称为动态连接。

No13:

所有方法调用中的目标方法在Class文件里面都是一个常量池中的符号引用,在类加载的解析阶段,会将其中的一部分符号引用转化为直接引用,这种解析能成立的前提是:方法在程序真正运行之前就有一个可确定的调用版本,并且这个方法的调用版本在运行期是不可改变的。

调用目标在程序代码写好、编译器进行编译时就必须确定下来,这类方法的调用称为解析;

No14:

在java语言中符合“编译器可知,运行期不可变”这个要求的方法,主要包括静态方法和私有方法两大类。

No15:

分派调用将揭示多态特征的一些最基本的体现。

No16:

分派----静态分派

public class StaticDispatch{
  static abstract class Human{}

  static class Man extends Human{}

  static class Woman extends Human{}

   public void sayHello(Human guy) {
     System.out.println("hello,guy");  
   }

   public void sayHello(Man guy) {
     System.out.println("hello,gentleman");  
   }

   public void sayHello(Woamn guy) {
     System.out.println("hello,lady");  
   }

   public static void main(String[] args){
      Human man = new Man();
      Human woman = new Woman();
      StaticDispatch sr = new StaicDispatch();
      sr.sayHello(man);
      sr.sayHello(woman);
   }
}        

运行结果

hello.guy
hello.guy

Human man = new Man();

Human为变量的静态类型,Man为变量的实际类型;

静态类型实际类型的区别:

1.静态类型的变化仅仅在使用时发生,变量本身的静态类型不会被改变,并且最终的静态类型是在编译期可知的

2.实际类型变化的结果在运行期才可确定,编译器在编译程序的时候并不知道一个对象的实际类型是什么

虚拟机(编译器)在重载时是通过参数的静态类型而不是实际类型作为判定依据的。静态类型是编译期可知的,在编译阶段,javac编译器会根据参数的静态类型决定哪个重载版本。

所有依赖静态类型来定位方法执行版本的分派动作称为静态分派。静态分派的典型应用是方法重载,静态分派发生在编译阶段,因此确定静态分派的动作实际上不是由虚拟机来执行的。

public class Overload {
    public static void sayHello(char arg) {
        System.out.println("hello char");
    }

    public static void sayHello(int arg) {
        System.out.println("hello int");
    }

    public static void sayHello(long arg) {
        System.out.println("hello long");
    }

    public static void sayHello(Character arg) {
        System.out.println("hello Character");
    }

    public static void sayHello(Serializable arg) {
        System.out.println("hello Serializable");
    }

    public static void sayHello(Object arg) {
        System.out.println("hello Object");
    }

    public static void sayHello(char... arg) {
        System.out.println("hello char...");
    }

    public static void main(String[] args) {
        sayHello('a');
    }
}

优先级顺序char->int->long->Character->Serializable->Object->char...

No17:

动态分派和重写有密切的关联。重载是静态的,重写是动态的。

No18:

方法的接受者与方法的参数统称为方法的宗量单分派是根据一个宗量对目标方法进行选择,多分派是根据多余一个宗量对目标方法进行选择。

public class Dispatch{
    static class QQ{}
    static class _360{}

    public static class Father{
        public void hardChoice(QQ arg){
            System.out.println("father choose qq");
        }

        public void hardChoice(_360 arg){
            System.out.println("father choose 360");
        }
    }

    public static class Son extends Father{
        public void hardChoice(QQ arg){
            System.out.println("son choose qq");
        }

        public void hardChoice(_360 arg){
            System.out.println("son choose 360");
        }
    }
    
    public static void main(String[] args){
        Father father = new Father();
        Father son = new Son();
        father.hardChoice(new _360());
        son.hardChoice(new QQ());
    }
}

运行结果

father choose 360
son choose qq

静态分派:1.静态类型是Father还是Son 2.方法参数是QQ还是360 所以是多分派类型

动态分派:1.此方法接受者实际类型是Father还是Son

No19:

总结:java语言是一门静态多分派、动态单分派的语言。

No20:

动态类型语言的关键特征是它的类型检查的主体过程是在运行期而不是编译器。

NegativeArraySizeException是运行时异常,NoClassDefFoundError是连接时异常。

No21:

静态类型语言在编译期确定类型,最显著的好处是编译器可以提供严谨的类型检查,这样与类型相关的问题能在编码的时候就及时发现,利于稳定性及代码达到更大规模。

动态类型语言在运行期确定类型,可以为开发人员提供更大的灵活性,某些在静态类型语言中需要大量“臃肿”代码来实现的功能,由动态类型语言来实现可能会更加清晰和简洁,清晰和简洁通常也就意味着开发效率的提升。

原文地址:https://www.cnblogs.com/anni-qianqian/p/7527612.html