深入拆解Java虚拟机
郑雨迪
Oracle 高级研究员,计算机博士
立即订阅
28017 人已学习
课程目录
已完结 39 讲
0/4登录后,你可以任选4讲全文学习。
开篇词 (1讲)
开篇词 | 为什么我们要学习Java虚拟机?
免费
模块一:Java虚拟机基本原理 (12讲)
01 | Java代码是怎么运行的?
02 | Java的基本类型
03 | Java虚拟机是如何加载Java类的?
04 | JVM是如何执行方法调用的?(上)
05 | JVM是如何执行方法调用的?(下)
06 | JVM是如何处理异常的?
07 | JVM是如何实现反射的?
08 | JVM是怎么实现invokedynamic的?(上)
09 | JVM是怎么实现invokedynamic的?(下)
10 | Java对象的内存布局
11 | 垃圾回收(上)
12 | 垃圾回收(下)
模块二:高效编译 (12讲)
【工具篇】 常用工具介绍
13 | Java内存模型
14 | Java虚拟机是怎么实现synchronized的?
15 | Java语法糖与Java编译器
16 | 即时编译(上)
17 | 即时编译(下)
18 | 即时编译器的中间表达形式
19 | Java字节码(基础篇)
20 | 方法内联(上)
21 | 方法内联(下)
22 | HotSpot虚拟机的intrinsic
23 | 逃逸分析
模块三:代码优化 (10讲)
24 | 字段访问相关优化
25 | 循环优化
26 | 向量化
27 | 注解处理器
28 | 基准测试框架JMH(上)
29 | 基准测试框架JMH(下)
30 | Java虚拟机的监控及诊断工具(命令行篇)
31 | Java虚拟机的监控及诊断工具(GUI篇)
32 | JNI的运行机制
33 | Java Agent与字节码注入
模块四:黑科技 (3讲)
34 | Graal:用Java编译Java
35 | Truffle:语言实现框架
36 | SubstrateVM:AOT编译框架
尾声 (1讲)
尾声 | 道阻且长,努力加餐
深入拆解Java虚拟机
登录|注册

08 | JVM是怎么实现invokedynamic的?(上)

郑雨迪 2018-08-08
前不久,“虚拟机”赛马俱乐部来了个年轻人,标榜自己是动态语言,是先进分子。
这一天,先进分子牵着一头鹿进来,说要参加赛马。咱部里的老学究 Java 就不同意了呀,鹿又不是马,哪能参加赛马。
当然了,这种墨守成规的调用方式,自然是先进分子所不齿的。现在年轻人里流行的是鸭子类型(duck typing)[1],只要是跑起来像只马的,它就是一只马,也就能够参加赛马比赛。
class Horse {
public void race() {
System.out.println("Horse.race()");
}
}
class Deer {
public void race() {
System.out.println("Deer.race()");
}
}
class Cobra {
public void race() {
System.out.println("How do you turn this on?");
}
}
(如何用同一种方式调用他们的赛跑方法?)
说到了这里,如果我们将赛跑定义为对赛跑方法(对应上述代码中的 race())的调用的话,那么这个故事的关键,就在于能不能在马场中调用非马类型的赛跑方法。
为了解答这个问题,我们先来回顾一下 Java 里的方法调用。在 Java 中,方法调用会被编译为 invokestatic,invokespecial,invokevirtual 以及 invokeinterface 四种指令。这些指令与包含目标方法类名、方法名以及方法描述符的符号引用捆绑。在实际运行之前,Java 虚拟机将根据这个符号引用链接到具体的目标方法。
可以看到,在这四种调用指令中,Java 虚拟机明确要求方法调用需要提供目标方法的类名。在这种体系下,我们有两个解决方案。一是调用其中一种类型的赛跑方法,比如说马类的赛跑方法。对于非马的类型,则给它套一层马甲,当成马来赛跑。
取消
完成
0/1000字
划线
笔记
复制
© 版权归极客邦科技所有,未经许可不得传播售卖。 页面已增加防盗追踪,如有侵权极客邦将依法追究其法律责任。
该试读文章来自付费专栏《深入拆解Java虚拟机》,如需阅读全部文章,
请订阅文章所属专栏。
立即订阅
登录 后留言

精选留言(23)

  • 秋天
    这个东西的应用场景是什么?讲的挺深,联系不起来知识
    2018-08-13
    48
  • Geek_488a8e
    这篇读的好吃力,我的一个建议,先抛出一个使用方法句柄的代码例子,然后再剖析代码在虚拟机的实际过程。自顶向下的讲,由浅入深,这篇直接自底向上了,咬咬牙读到最后,才发现这是类似反射的模拟方法调用的方法

    作者回复: 多谢建议!

    2018-08-30
    13
  • Yoph
    方法句柄VS反射VS代理:
    从访问控制层面来讲,反射需要调用setAccesible(),可能会受到安全管理器的禁止警告;代理有些情况下通过内部类实现,但是内部类只能访问受限的函数或字段;而方法句柄则在上下文中对所有方法都有完整的访问权限,并且不会受到安全管理器的限制,这是方法句柄的优势之一。
    从执行速度层面来讲,在上一篇中老师也讲到了反射的性能会受到参数方法、类型的自动装箱和拆箱、方法内联的影响,相对来讲反射算是执行较慢的了(当然并没有和方法句柄通过执行具体操作示例作对比,可能在不同的JVM配置情况下执行情况不一样,比如解释器模式或编译模式下等);通过代理的方式因调用JAVA函数实现,速度与其它调用函数的速度是一样的,相对较快;而方法句柄可能不会有代理方式那样的执行速度快,但同样会受到JVM等不同的配置导致速度不同,但从JVM设计者的角度来说,应该是力求达到像调用函数一样快的速度,目前可能是达不到的。
    从类的开销层面来讲,代理通常声明多个类,需要占用方法区,而方法句柄并不需要像代理一样有多个类的开销,不需要方法区的开销。
    2018-11-13
    10
  • 三木子
    那么方法句柄是否可以取代反射了呢?
    2018-08-08
    7
  • 雨亦奇
    个人觉得还是按老师的课程安排来走吧。跳来跳去的讲可能会零散不系统。上面两位的说法我不赞同。
    2018-08-09
    6
  • 飞翔明天
    老师说后面会简单一点,但是我到这篇,感觉好难,看不懂了
    2019-07-04
    2
  • Yoph
    方法句柄VS反射VS代理:
    从访问控制层面来讲,反射需要调用setAccesible(),可能会受到安全管理器的禁止警告;代理有些情况下通过内部类实现,但是内部类只能访问受限的函数或字段;而方法句柄则在上下文中对所有方法都有完整的访问权限,并且不会受到安全管理器的限制,这是方法句柄的优势之一。
    从执行速度层面来讲,在上一篇中老师也讲到了反射的性能会受到参数方法、类型的自动装箱和拆箱、方法内联的影响,相对来讲反射算是执行较慢的了(当然并没有和方法句柄通过执行具体操作示例作对比,可能在不同的JVM配置情况下执行情况不一样,比如解释器模式或编译模式下等);通过代理的方式因调用JAVA函数实现,速度与其它调用函数的速度是一样的,相对较快;而方法句柄可能不会有代理方式那样的执行速度快,但同样会受到JVM等不同的配置导致速度不同,但从JVM设计者的角度来说,应该是力求达到像调用函数一样快的速度,目前可能是达不到的。
    从类的开销层面来讲,代理通常声明多个类,需要占用方法区,而方法句柄并不需要像代理一样有多个类的开销,不需要方法区的开销。
    2018-11-13
    2
  • Yoph
    方法句柄其实就是可以取得与反射相同的效果,不过方法句柄使用的代码更简洁。使用方法句柄,可以去掉反射中很多套路化的代码,提高代码的可读性。
    2018-11-13
    2
  • ext4
    雨迪,我看了一下MethodHandle的增操作,即你所提到的bindTo这个API,它貌似只能用于为virtual method绑定第一个参数(即caller的this*指针),并不能普适地为方法绑定一个任意参数(例如把参数列表(int, int)里的第一个参数绑定为常数4)。那么你例子中所提到的更为一般性的柯里化又是怎么实现的呢?

    作者回复: 多谢指出 :)
    bindTo确实限制了只能使用引用类型,而且正如你所说普遍是用来绑定this的。但是由于方法句柄不区分调用者和参数,所以还是可以滥用的。

    你可以试试用Integer,然后使用静态方法,或者在使用virtual方法时将bindTo返回的方法句柄再bindTo一个Integer.valueOf(4)

    BoundMethodHande里有各种非公有的bindArgument*方法,感兴趣可以了解一下

    2018-08-09
    2
  • lmtoo
    invokedynamic和MethodHandle有啥关系?
    2019-06-23
    1
  •  素丶  
    暂时只能想到把他绑定到一个固定的 “Foo” 实例上。。。。
    import java.lang.invoke.*;

    public class Foo {
      public void bar(Object o) {
      }

      public static void main(String[] args) throws Throwable {
        MethodHandles.Lookup l = MethodHandles.lookup();
        MethodType t = MethodType.methodType(void.class, Object.class);
    Foo foo = new Foo();
        MethodHandle mh = l.findVirtual(Foo.class, "bar", t).bindTo(foo);

        long current = System.currentTimeMillis();
        for (int i = 1; i <= 2_000_000_000; i++) {
          if (i % 100_000_000 == 0) {
            long temp = System.currentTimeMillis();
            System.out.println(temp - current);
            current = temp;
           }
           mh.invokeExact(new Object());
        }
      }
    }
    2018-11-12
    1
  • 随心而至
    老师,你的是基于JDK 10的吗?我JDK8的DUMP_CLASS_FILES看起来和您的有点不一样。
    2019-09-27
  • neohope
    性能测试下来,反射》final MethodHandle》MethodHandle
    2019-08-24
  • 大俊stan
    how do you turn this on 笑死了
    2019-08-17
  • QQ怪
    想问下老师,为啥在jdk1.8上面分别用反射和方法句柄测试,发现执行次数量特别大的时候方法句柄执行效率反而比反射差,执行数量小的时候比反射执行效率高,不知道为什么,请老师点拨?
    2019-03-21
  • ゞ﹏雨天____゛
    有些内容,第一遍读总是看不懂,听不明白,当你多次读了之后,并查阅相关内容后,你会发现雨迪老师讲的内容,真的很到位,值得学习。
    2019-03-16
  • invokedynamic与方法句柄的关系是怎么样的?f方法句柄替invokedynamic抽象出调用点?
    2019-02-26
  • Cy190622
    当使用方法句柄时,我们不关心方法句柄指向方法的类名或者方法名。
    那怎么区分不同类同名方法
    2019-01-21
  • 松鼠
    反射的权限检查是在编译期完成的,怎么会有权限检查的开销?
    2019-01-03
  • Geek_987169
    "方法句柄 API 有一个特殊的注解类 @PolymorphicSignature。在碰到被它注解的方法调用时"这句话的"被它注解的方法调用时"是不是就是"调用的方法被它注解时"?
    2018-09-19
收起评论
23
返回
顶部