深入理解JVM - 动态类型语言

简介: 深入理解JVM - 动态类型语言

前言


上一节讲述了栈桢和分派的细节,这一节我们来讲讲自java语言诞生新增加的新语言特性:动态类型语言支持,这一节将会根据动态语言的特性以及相关的介绍同时讲述jvm一个重要的指令:invoke dynamic指令。但是需要注意的是:invokedy namic指令面向 的主要服务对象并非Java语言,而是其他Java虚拟机之上的其他动态类型语言


概述


  1. 介绍什么是动态类型语言,以及java为什么是静态语言的讲解。
  2. 介绍invokeDynamic指令在实际案例中的运用
  3. 介绍java实现动态语言调用的一些曲线救国的手段。


动态类型语言



什么是动态类型语言


动态类型语言的关键特征是它的类型检查的主体过程是在运行期而不是编译期。而java就是典型的静态类型语言,需要在编码的过程中确定的,静态的语言也意味着所有的类型在编译器必须确定。


为什么java是静态类型?


这里牵扯到一个问题就是为什么java是静态类型呢?我们可以看一下invokeVitual命令,这个命令根据如下的内容,确定一个属性的全类名,以及类型,在符号引用的阶段可以看到基本的内容:


invokevirtual #4; //Method java/io/PrintStream.println:(Ljava/lang/String;)V

这些符号引用在翻译为直接引用是需要确切的类型的,所以在早期的java天生缺乏动态语言的支持。


动态语言类型支持


java在jdk7之后加入了动态语言支持,关于加入动态语言类型的支持核心是使用invoke dynamic命令, 这种方式使用了类似曲线救国的方式,也是为兼容考虑不得不做的一种妥协,比如最常见的类型数组,在java中我们必须声明确数组存放的类型,而jdk引入了invokeDynamic这个指令之后,就可以完成对于一个方法参数的动态调用。

动态类型语言是可以让对象的类型可以在运行时候再确定,比如JS和Python的var。


invokedynamic指令


下面来说下invoke这个指令是如何实现动态类型语言的,在java中是无法把一个函数作为参数传递的,更多的方式使用类似实现接口的方式进行处理,而新的指令在某种程度上是使用类似MethodHandle的方式进行处理的,MethodHandle是对通过字节码的方法指令调用的模拟,但和反射不同的是反射是基于Java语言服务的,而MethodHandler则是服务于所有虚拟机上的一种语言。

每一处含有invokedynamic指令的位置都被称作“动态调用点(Dynamically-Computed Call Site)”, 这条指令的第一个参数不再是代表方法符号引用的CONSTANT_Methodref_info常量,而是变为JDK 7 时新加入的CONSTANT_InvokeDynamic_info常量,从这个新常量中可以得到3项信息:引导方法 (Bootstrap Method,该方法存放在新增的BootstrapMethods属性中)、方法类型(MethodType)和 名称

为了更好的理解这个命令,下面我们来看下实际运行过程当中的应用,比如在jdk8中引入的lambada表达式和默认方法就是通过invokedynamic命令实现的,但是使用jdk8的实现看起来比较难以理解,下面来看一下书中给的一段案例代码:


Constant pool:
#121 = NameAndType #33:#30 // testMethod:(Ljava/lang/String;)V #123 = InvokeDynamic #0:#121 // #0:testMethod:(Ljava/lang/String;)V
public static void main(java.lang.String[]) throws java.lang.Throwable; Code:
stack=2, locals=1, args_size=1
0: ldc #23 // String abc
2: invokedynamic #123, 0 // InvokeDynamic #0:testMethod: (Ljava/lang/String;)V 7: nop
8: return
public static java.lang.invoke.CallSite BootstrapMethod(java.lang.invoke.Method Handles$Lookup, java.lang.Strin Code:
    stack=6, locals=3, args_size=3
0: new
3: dup
4: aload_0
5: ldc
7: aload_1
8: aload_2
9: invokevirtual #65
12: invokespecial #71 15: areturn
#63
#1
// class java/lang/invoke/ConstantCallSite
// class org/fenixsoft/InvokeDynamicTest
// Method java/lang/invoke/MethodHandles$ Lookup.findStatic:(Ljava/lang/Cl // Method java/lang/invoke/ConstantCallSite. "<init>":(Ljava/lang/invoke/M
复制代码


从上面的方法调用可以看到,使用的是invokeDynamic的调用指令以及参数为第123项的常量,比如如下的内容:


2: invokedynamic #123, 0 // InvokeDynamic #0:testMethod:(Ljava/lang/String;)V

而BootstrapMethod()方法中指令将会产生testMethod()方法,当然这个方法在java源码中是看不见的,而是由invokeDynamic动态生成的一个方法,当指令完成对应的方法调用之后,这个指令的调用过程也宣告结束了。


总结


本文的内容比较啊间断,主要针对动态类型语言做了一个补充,内容比较剪短,至此,jvm的内容大致以及全部讲述完毕,而关于书中的最后一节并发编程的描述,个人将会放到《并发编程实战》中进行总结(又得回去看一遍)。


写在最后


invokeDynamic主要服务的是其他语言的接入,但是从实际效果来看不是十分的理想。

相关文章
|
3月前
|
自然语言处理 算法 安全
编程语言中的静态和动态类型语言
【7月更文挑战第14天】本文介绍静态与动态类型语言对比。类型检查效率是关键,一些系统可能在极端情况下慢。自动化与高效算法的研究持续进行.
36 5
编程语言中的静态和动态类型语言
|
3月前
|
存储 Rust JavaScript
Rust 问题之TypeScript 代码,变量 s 存储在栈内存中还是堆内存中如何解决
Rust 问题之TypeScript 代码,变量 s 存储在栈内存中还是堆内存中如何解决
|
4月前
|
Rust 安全 开发者
探索Rust语言的内存安全特性
【6月更文挑战第8天】Rust语言针对内存安全问题提供了创新解决方案,包括所有权系统、借用规则和生命周期参数。所有权系统确保值与其所有者绑定,防止内存泄漏;借用规则保证同一时间只有一个可变引用或多个不可变引用,消除数据竞争和野指针;生命周期参数则强化了引用的有效范围,提升安全性。通过这些特性,Rust帮助开发者编写出更健壮、安全的高性能软件,有望成为系统编程领域的领头羊。
|
5月前
|
Rust 安全 Java
Rust 语言的类型系统
假如让你设计编程语言的类型,你会怎么做? 要定义哪些类型? 类型之间如何交互? 是否需要类型推断? 类型系统是编程语言中用于定义和控制类型的一组规则。
Rust 语言的类型系统
|
5月前
|
JavaScript 前端开发 C语言
Python 到底是强类型语言,还是弱类型语言?
Python 到底是强类型语言,还是弱类型语言?
36 2
|
5月前
|
JavaScript 前端开发 Java
静态语言和动态语言,解释和编译
静态语言和动态语言,解释和编译
73 1
|
5月前
|
JavaScript 前端开发 Java
深入理解JVM - 动态类型语言
深入理解JVM - 动态类型语言
69 0
|
5月前
|
存储 Rust 编译器
Rust 笔记:Rust 语言中的常量与变量
Rust 笔记:Rust 语言中的常量与变量
86 0
|
存储 Rust 编译器
Rust 笔记Rust 语言中的常量与变量
本文介绍 Rust 语言中的常量与变量。
120 0
|
IDE Java 大数据
Java 答疑:编译器和解释器有何区别?Java 语言属于编译型编程语言还是解释型编程语言?
Java 答疑:编译器和解释器有何区别?Java 语言属于编译型编程语言还是解释型编程语言?
248 0
Java 答疑:编译器和解释器有何区别?Java 语言属于编译型编程语言还是解释型编程语言?
下一篇
无影云桌面