• 虚拟机字节码执行


    运行时栈帧结构
    栈帧是用来支持虚拟机进行方法调用和方法执行的数据结构,它是虚拟机运行时数据区中的虚拟机栈的栈元素。
    栈帧存储了方法的局部变量表、操作数栈、动态连接、和方法返回地址等信息。
    方法的调用到执行完成对应一个栈帧的入栈到出栈过程。
    局部变量表
    一组变量值存储空间,用于存放方法参数和方法内部定义的局部变量。
    在编译期确定大小,max_locals
    单位slot(32位)
    八种数据的类型:byte short int char boolean float reference returnAddress
    reference(引用类型):对象实例的引用:找到实例对象在堆中初始地址索引;找到对象方法区的类型信息
    64位数据类型long double,可用两个连续的slot,原子操作?,局部变量建立在线程的堆栈上不存在线程安全问题,在类加载校验阶段会抛出异常,不允许访问其中的一个,
    执行实例方法时,那局部变量表中第0位索引:指向实例对象的应用(可用this调用此引用),从第1位开始分配局部变量Slot,参数表-->再根据内部定义的局部变量顺序+作用域分配其余的Slot
    为了节省空间,Slot是可以重用的:场景方法中作用域内的局部变量超出作用域之后,
    副作用是影响GC行为,延迟GC,超出作用域后,作用域内局部变量slot未被占用,GC可能不会回收
    不使用的对象应手动赋值为null,但是;以恰当的变量作用域来控制变量回收时间才是最优雅的解决方案,“公有设计,私有实现”,赋值为null的操作在经过JIT优化后会被消除,使得设置null变得毫无意义,而且JIT后本地代码GCRoot和解释执行期有很大的区别。
    操作数栈
    先入后出
    编译期确定大小,写入到Code属性的max_stacks数据项,任何时候,操作数栈的深度都不会超过max_stacks
    每个栈元素可以是任意类型的java数据类型,32位数据类型栈容量为1,,64为2
    方法开始执行时,操作数栈时空的,执行过程中,会有各种字节码指令往操作数栈中写入和提取内容,入栈、出栈操作。
    编译期:操作数栈中的元素数据类型必须与字节码指令的序列严格匹配。
    概念模型中连个栈帧作为虚拟机栈的元素是完全独立的,但是大多数虚拟机实现都会做一些优化,两栈部分局部变量表和部分操作数栈重叠。
    动态链接
    每个栈帧都包含一个指向运行时常量池中该栈帧所属方法的引用。
    静态解析:Class文件中常量池存在大量的符号引用,类加载或者第一次使用时转化为直接引用
    动态链接:Class文件中常量池存在大量的符号引用,运行期间转化为直接引用
    方法返回地址
    正常完成出口:执行引擎遇到任意一个方法返回的字节码指令,导致方法退出。
    异常完成出口:在本方法的异常表中没有搜索到匹配的异常处理器,导致方法退出。
    附加信息
     
    方法调用:
    方法调用并不等同与方法执行,方法调用阶段唯一任务:确定被调用方法的版本(调用哪一个方法)
    解析
    类加载的解析阶段,会将一部分符号引用转化为直接引用--编译期可知,运行期不可变
    虚拟机提供5条方法调用字节码指令
    invokestatic:调用静态方法
    invokespecial:调用实例构造器<init>方法,私有方法和父类方法
    invokevirtual:调用所有的虚方法
    invokeinterface:调用接口方法,会在运行时再确定一个实现此接口的对象。
    invokedynamic:现在运行时动态解析出调用点限定符所引用的方法,然后在执行该方法。
    其中被invokestatic、invokespecial指令调用的方法,都可以在解析阶段中确定唯一的调用版本:
     
    方法调用
    静态解析
    静态解析成立前提:方法在程序真正运行之前就有一个可确定的调用版本,并且这个方法的调用版本在运行期是不可改变的。即调用目标在程序代码写好,编译器进行编译时必须确定下来的。
    符合“编译器克制,运行期不可变”的方法:静态方法,私有方法、实例构造器、父类方法--非虚方法
    解析解析一定是一个静态的过程;分派调用则可能是静态的也可能是动态的
    分派
    Human man = new Man();

    将Human称为变量man的静态类型

    将Man称为变量man的实际类型

    静态分派
    所有以来静态类型来确定方法执行版本的分派过程称为静态分派。
    静态分派的典型应用就是方法重载。                          
    静态分派发生在编译阶段,因此确定静态分派的动作实际上不是有虚拟机来执行的。
    重载版本并不唯一,往往只能确定一个更加合适的版本,产生模糊结论的只要原因是字面量不需要定义,即字面量没有显示的静态类型,只能根据语言的规则去理解和推断。
    public static void sayHello(char arg){
        System.out.println("hello char");
    }
    
    public static void sayHello(Character arg){
        System.out.println("hello Character ");
    }
    
    public static void main(String[] args){
        sayHello('a');//字面量'a'没有显示的静态类型,只能根据语言规则去理解与推断
    }
    //基本类型及基本类型转换-->封装类型-->序列化类型-->父类-->变长参数
    //(char-int-long)-->Character-->Serializable-->Object-->char..

    解析和分派并不是二选一的排他关系,静态方法是静态解析的,但静态方法重载后选择合适版本是静态分派的。

    动态分派
    运行期根据实际类型确定方法执行版本的分派过程称为动态分派。
    动态分派的典型应用就是方法重写。 (在继承关系自下向上查找)
     单分派与多分派
    方法的接受者与方法的参数统称为方法的宗量
     单分派是根据一个宗量对目标方法进行选择,多分派则是根据多于一个宗量对目标方法进行选择。
    Java重载根据静态类型+参数确定目标方法的,即根据两个宗量进行选择属于静态多分派。
    Java重写根据实际类型来确定目标方法的,即根据一个宗量进行选择属于动态单分派。
    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[] arg){
            Father father = new Father();
            Father son = new Son();
            father.hardChoise(new _360());//(重载)编译阶段根据静态类型father+参数_360确定目的方法,静态多分派
            son.hardChoise(new QQ());//由于在编译阶段时已经确定了father.hardChoise(QQ),运行阶段只需要根据实际类型Son就可以确定目的方法son.hardChoise(QQ),动态单分派
        }
    }
    Java语言是一门静态多分派,动态单分派的语言
     
    动态类型支持,典型的动态语言JavaScript,javaScript,类型检查在运行期。
    字节码生成技术与动态代理的实现
    字节码生成技术,常见的CGLib,ASM之类的字节码类库,JDK中javac命令就是字节码生成技术的“老祖宗”
    现在开发人员都是用过Spring框架,其中AOP的实现方式有两种:JDK自带的动态代理及其拓展类库CGLib(主要解决动态代理方式实现代理时的目标类必须实现接口的缺点)
  • 相关阅读:
    让UILabel的大小自适应字符串长度
    Cocos2dx中的分辨率学习
    关键帧动画-五角星动画-在层上画出五角星
    .net WINFORM 界面怎么做凹凸效果的分割线?就是横线
    游标Cursor 使用小例 (SQLServer)
    winform屏蔽Alt+F4组合键以防止用户关闭对话框
    SQL Server 2005 数据类型和.Net数据类型的对应关系
    php $GLOBALS 超全局变量的理解
    我改行了
    PHP $_FILES详解
  • 原文地址:https://www.cnblogs.com/wqff-biubiu/p/10402874.html
Copyright © 2020-2023  润新知