导航菜单
首页 >  探秘Java虚拟机  > Java虚拟机生态技术及其7种编程语言探秘(上)

Java虚拟机生态技术及其7种编程语言探秘(上)

【引言】

Java虚拟机(JVM)使计算机能够运行Java程序以及其他语言编写的程序,这些程序被编译成Java字节码。 

JVM由一个规范来描述JVM实现中所需要的内容。 

JVM规范可以确保Java程序在不同实现之间的互操作性,因此使用Java开发工具包(JDK)的程序作者不必担心底层硬件平台的特殊性。 

JVM参考实现是由OpenJDK项目以开放源码的形式开发的,包括一个名为HotSpot的JIT编译器。Oracle公司提供的商业上支持的Java版本都是基于OpenJDK运行库。Eclipse OpenJ9是OpenJDK项目的另一个开源的JVM。

 

【JVM规范】

Java虚拟机是一个抽象的虚拟计算机, 它需要遵循一些JVM规范。 

其中所使用的垃圾收集算法和Java虚拟机指令的任何内部优化如翻译成机器代码都没有被事先指定。 

这样做的主要原因是为了避免对实现者造成不必要的约束。任何Java的应用程序只能在Java虚拟机的内部运行,这个虚拟机必须遵守这些的规范。 

从Java平台标准版(J2SE)5.0开始,在Java社区进程下, JVM规范的修改作为JSR 924开发, 截止到2006年,为支持类文件格式(JSR 202)提出的修改作为JSR 924的维护版本进行。

发布的JVM规范蓝皮书前言中说:

“我们的意图是,此规范应该充分地描述Java虚拟机,以便使兼容的无尘室实现成为可能。Oracle提供了验证Java虚拟机实现是否正确的测试。”

Oracle的一个JVM被命名为HotSpot,另一个是JRockit,是从BEA Systems继承的。 

无尘室的Java实现包括Kaffe、OpenJ9和Skelmir的CEE-J。 

Oracle拥有Java商标,并该商标来认证实现套件是否完全符合Oracle的规范。

 

【类加载器】

JVM 字节代码的组织单位之一是类。一个类加载器实现必须能够识别和加载任何符合Java类文件格式的内容。除了必须要识别类文件之外,也可以自由得添加识别其他二进制形式的实现。

类加载器有三个基本任务,它们按照严格的顺序执行: 

1.  加载:找到并导入类型的二进制数据

2.  连接:执行核查、准备和可选的引用解析。

a)  验证:确保输入类型的正确性。

b)  准备工作:为类变量分配内存,并将内存初始化为默认值。

c)  解析:将类型中的符号引用转换为直接引用。

3.  初始化:调用Java代码,将类变量初始化为合适的起始值。

一般来说,类加载器有两种类型:bootstrap类加载器和用户定义的类加载器。 

每个Java虚拟机实现都必须有一个bootstrap类加载器,能够加载可信类。Java虚拟机规范并没有规定类加载器应该如何定位类。

 

【虚拟机架构】

JVM操作的是原始数据(整数和浮点数)和引用。 

JVM从根本上来说是一个32位的机器。 

Long和double类型是64位的,支持原生的,但由于每个单元都是32位,所以在一个帧的本地变量或操作符堆栈中消耗了两个存储单元。 

boolean是作为8位字节值来操作的,0代表false,1代表true(虽然自从Java虚拟机规范第二版澄清了这个问题之后,boolean就被视为一种类型,但是在编译和执行的代码中,除了方法签名中的名字和boolean数组的类型外,boolean和字节之间没有什么区别。

布尔数组的类型是boolean[],但每个元素使用8位,JVM没有将boolean打包到位数组中的内置能力,所以除了类型不同外,它们的执行和行为与字节数组相同。在所有其他的使用中,布尔类型实际上对JVM来说是未知的,因为所有对布尔类型进行操作的指令也都是用来对字节进行操作的。 

JVM有一个垃圾收集堆,用于存储对象和数组。代码、常量和其他类数据都存储在 "方法区"中。方法区在逻辑上是堆的一部分,但具体的实现可能会将方法区与堆分开处理,因为一般不会对“方法区”进行垃圾收集。 

每个JVM线程也有自己的调用堆(为了清楚起见,称为"Java虚拟机堆"),它存储的是帧。每次调用一个方法时都会创建一个新的帧,当该方法退出时,该帧会被销毁。 

每个帧提供了一个"操作符栈"和一个"本地变量"的数组。操作符栈用于管理计算的操作符以及接收被调用方法的返回值,而局部变量的作用与寄存器相同,也用于传递方法参数。因此,JVM既是一个栈机,又是一个寄存器机。

 

【字节码指令】

JVM有以下几组任务的指令: 

1.  加载和存储

2.  算术

3.  类型转换

4.  对象创建和操作

5.  操作数栈管理(push/pop)

6.  控制传输(分支)

7.  方法调用和返回

8.  抛出异常

9.  基于监控器的并发性 

其目的是二进制的兼容性。每个特定的主机操作系统都需要有自己的JVM和运行时的实现。这些JVM在语义上对字节码的解释方式是一样的,但具体实现可能不同。 

比模拟字节码更复杂的是,必须兼容并有效地实现必须映射到每个主机操作系统的Java核心API。 

这些指令是操作的是一组通用的抽象数据类型,而不是任何特定指令集架构的本机数据类型。

 

【JVM编程语言】

JVM编程语言是指任何具有可以用Java虚拟机托管的有效类文件来表达功能的语言。类文件包含Java虚拟机指令(Java字节码)和符号表,以及其他辅助信息。类文件格式是一种与硬件和操作系统无关的二进制格式,用于表示已编译的类和接口。 

有几种JVM编程语言,既有移植到JVM的老语言,也有完全新的语言。 

JRuby和Jython可能是现有语言中最著名的老语言移植,即Ruby和Python的移植。 

在新语言中,Clojure、Apache Groovy、Scala和Kotlin是最受欢迎的语言。JVM语言的一个显著特点是它们之间是相互兼容的,因此,Scala库可以与Java程序一起使用,反之亦然。 

Java 7 JVM在Java平台上实现了JSR 292:支持动态类型化语言,这是一个在JVM中支持动态类型化语言的新功能。该功能是在达芬奇机器项目中开发的,其任务是扩展JVM,使其支持Java以外的语言。

 

【JRuby】

JRuby类似于标准的Ruby解释器,区别只是用Java语言来编写。 JRuby与Ruby有一些相同的概念,包括面向对象编程和动态类型。其关键的区别在于,JRuby与Java紧密结合,可以直接从Java程序中调用它。

 

安装

l  下载:https://www.jruby.org/download

l  将JRuby提取到一个目录中。

l  将该目录的bin子目录添加到你的路径。

l  测试一下:jruby -v 

JRuby调用Java

JRuby的一个强大功能是它能够调用Java平台的类。要做到这一点,首先必须通过调用 "require 'java'"来加载JRuby的Java支持。下面的例子创建了一个带有JLabel的Java JFrame:

        require 'java'

 

        frame = javax.swing.JFrame.new

        frame.getContentPane.add javax.swing.JLabel.new('Hello, World!')

        frame.setDefaultCloseOperation javax.swing.JFrame::EXIT_ON_CLOSE

        frame.pack

        frame.set_visible true

 

JRuby还允许用户使用更类似于Ruby的下划线方法命名来调用Java代码,并将JavaBean属性作为属性来引用。

        frame.content_pane.add label

        frame.visible = true

 

从Java中调用JRuby

JRuby可以很容易地被Java调用,这可以通过使用JSR 223 Scripting for Java 6或Apache Bean Scripting框架来做到。

 

        // 使用JSR 233脚本编写Java 6的例子

        ScriptEngineManager mgr = new ScriptEngineManager();

        ScriptEngine rbEngine = mgr.getEngineByExtension("rb");

        try {

            rbEngine.eval("puts 'Hello World!'");

        } catch (ScriptException ex) {

            ex.printStackTrace();

        }

 

 

【Jython】 

Jython是Python编程语言的一个实现,运行在Java平台上。在1999年之前被称为JPython。

Jython程序可以导入和使用任何Java类。除了一些标准模块外,Jython程序使用的是Java类而不是Python模块。Jython几乎包含了标准Python编程语言发行版中的所有模块,只是缺少一些最初在C语言中实现的模块。Jython可以按需或静态地将Python源代码编译成Java字节码。

 

安装

下载:https://www.jython.org/download

java -jar D:/tool/jython-standalone-2.7.2.jar

在Java应用程序中运行Python代码

import org.python.util.PythonInterpreter;

 

public class JythonHelloWorld {

  public static void main(String[] args) {

    try(PythonInterpreter pyInterp = new PythonInterpreter()) {

      pyInterp.exec("print('Hello Python World!')");

    }

  }

}

 

在Python代码中运行Java

from java.lang import System # Java import

 

print('Running on Java version: ' + System.getProperty('java.version'))

print('Unix time from Java: ' + str(System.currentTimeMillis()))

 

 

【Clojure】

Clojure是Java平台上的Lisp编程语言的一种现代、动态、功能化的实现。 

和其他Lisp编程语言一样,Clojure把代码当作数据来处理,并有一个Lisp宏系统。 

目前的开发过程是由社区驱动的,由Rich Hickey监督。

Clojure提倡不可变性,使用不可变的数据结构,鼓励程序员对身份及其状态进行显式管理。

这种注重用不可变值和显式时间递进构造进行编程的做法是为了开发出更健壮的程序,特别是在开发并发程序时追求程序简单、快速。 

它的类型系统完全是动态的,最近在努力寻求渐进式类型化的实现. 

Clojure的商业支持是由Cognitect提供的,Clojure会议每年都会在全球范围内举办,其中最著名的是Clojure/conj。

 

安装

步骤:https://clojure.org/guides/getting_started

示例1:

 

user=> (+ 3 4)

7

user=> (+ 10 *1)

17

user=> (+ *1 *2)

24

示例2:

(ns clojure.examples.hello

    (:gen-class))

 (defn hello-world []

    (println "Hello World"))

 (hello-world)

 

示例3:

(ns reader.tasklist

    (:gen-class ; 

     :extends org.xml.sax.helpers.DefaultHandler ; 

     :state state ; 

     :init init) ; 

    (:use [clojure.java.io :only (reader)])

    (:import [java.io File]

             [org.xml.sax InputSource]

         [org.xml.sax.helpers DefaultHandler]

         [javax.xml.parsers SAXParserFactory]))

 

【Groovy】

Apache Groovy是一种与Java语法兼容的面向对象编程语言,适用于Java平台。它既是一种静态语言,也是一种动态语言,具有类似于Python、Ruby和Smalltalk的特性。 

它既可以作为Java平台的编程语言,也可以作为Java平台的脚本语言,被编译成Java虚拟机(JVM)字节码,并可与其他Java代码和程序库无缝互操作。 

Groovy使用一种类似于Java的卷标式语法。Groovy支持闭合(Closure)、多行字符串和嵌入字符串中的表达式。 

Groovy的一部分强项在于它的AST转换,通过注解触发。

 

功能

大多数有效的Java文件也是有效的Groovy文件。虽然这两种语言很相似,但Groovy代码可以更紧凑,因为它不需要Java所要求的所有元素,这使得Java程序员可以从熟悉的Java语法开始,逐步学习Groovy,然后再掌握

相关推荐: