• java-方法调用


    解析调用

    解析调用是指对那些“编译器可知,运行期不变”的方法的调用,这些方法在编译期就可以确定,并且在解析阶段转换为直接引用,之后就不会改变了。可以进行解析调用的方法有:构造方法、私有方法、final方法、父类方法,这些方法在运行期都是唯一的,不会改变。

     1 public class TempClazz {
     2   public static void main(String[] args) {
     3     TempClazz tempClazz = new TempClazz();
     4     System.out.println("main");
     5     tempClazz.fun();
     6   }
     7   public void fun() {
     8     A.fun();
     9   }
    10 
    11   private static class A {
    12     static {
    13       System.out.println("this is A");
    14     }
    15     public static void fun() {
    16       System.out.println("this is A fun");
    17     }
    18   }
    19 }

    -XX:+TraceClassLoading输出
    [Loaded com.zyong.TempClazz from file:......]
    main
    [Loaded com.zyong.TempClazz$A from file:......]
    this is A
    this is A fun

    上面测试中,A.fun()的解析并不发生在TempClazz加载阶段,而发生在A.fun()的调用阶段,解析后,A的常量池中对A.fun()的符号引用就会被转换为直接引用,在之后的运行中就可以直接调用了。

    分派调用

    java是一门单分派语言,分派是一门语言将方法调用连接到方法定义的一种方式。通常由接收者(方法调用者)、方法名(包含类名,比如java.lang.Object.hashCode)、实参等因素就可以确定分派,单分派就是只有一个因素决定的分派,多分派就是有多个因素决定的分派。

    分派严格来说是动态的,目的就是在运行过程中找到方法定义,并没有静态分派一说。之所以是单分派,是由于java方法的调用只看调用者的实际类型,而不看实参的实际类型(只关注实参的静态类型,这在编译期就已确定)。如果既关注调用者的实际类型,又关注实参的实际类型就是多份派了。

    java的多态就是通过分派,在运行时从多个方法版本中找到匹配的那一个。

    解析调用和分派调用是不可能同时存在的,分派需要运行时确定方法版本(invokevirtual首先寻找实际类型,然后在实际类型中找对应的方法签名,最终返回直接引用),而解析在解析之后就不会再变了(解析会直接改变常量池中的符号引用为直接引用,而不像动态解析那样需要运行时去寻找直接引用)。

     java对象在内存中的样子

    如下图就是java对象中字段的布局

    子类新增字段也没什么区别

    对于方法的存储,如果每个对象都存储一份方法代码太低效了,方法逻辑是可以共享的,在每个对象中都会有一个Vtable(虚方法表)的引用,这个引用指向Vtable,Vtable中存储了各个方法的实际入口地址(如何在表中查找呢?)。虚表是分派调用才需要的,解析调用在解析阶段将符号引用替换为直接引用,之后的访问都会直接使用直接引用。

    基于栈的解释器执行过程

    1 public int calc() {
    2     int a = 100;
    3     int b = 200;
    4     int c = 300;
    5     return (a+b)*c;
    6 }

     解释器中,方法的执行涉及jvm指令、程序计数器、操作栈、局部变量表等。

    如下图,程序计数器现在是0,表示执行偏移为0的jvm指令(下一条指令istore_1的偏移为2,可以理解为bipush 100是两条指令,bipush一条,100一条),bipush是将单字节整型常量压入操作栈中,istore_1是将栈顶的整型值出栈并存放在第1个局部变量中(第0个存this),sipush是压入4字节整型,iload_1是将局部变量表中的第1个变量复制到栈顶,iadd是将栈中头两个栈顶元素出栈并做整型加法再将结果入栈,imul是做整型乘法,ireturn是将栈顶的整型值返回。

    参考:http://www.studytonight.com/java/dynamic-method-dispatch.php

      https://www.programcreek.com/2011/11/what-do-java-objects-look-like-in-memory/

  • 相关阅读:
    旷视科技python开发,python数据清理和数据清洗面试题【杭州多测师_王sir】【杭州多测师】
    App三种启动场景:冷启动、热启动、温启动【杭州多测师_王sir】【杭州多测师】
    adb常用的命令【杭州多测师_王sir】【杭州多测师】
    采购预付F48
    做题开悟
    发票自动结算
    SAP 评估收货结算(ERS)
    R语言用贝叶斯线性回归、贝叶斯模型平均 (BMA)来预测工人工资|附代码数据
    R语言文本挖掘NASA数据网络分析,tfidf和主题建模|附代码数据
    【视频】文本挖掘:主题模型(LDA)及R语言实现分析游记数据|附代码数据
  • 原文地址:https://www.cnblogs.com/holoyong/p/7505908.html
Copyright © 2020-2023  润新知