Java虚拟机是否与编程语言无关?

10

可以说Java虚拟机最初是为Java编程语言设计的,但现在其他开发者也能够编写编译成Java字节码的编程语言,例如Scala,Jython和JRuby。

Java字节码中仍然包含面向对象的引用,如接口,方法,字段。例如invokespecial是对“对象”方法的调用。

它不是一个纯栈式虚拟机,也没有纯语言无关指令集。例如,一个纯FORTH实现只会有堆栈操作。

问题在于JVM是否是具备语言无关性的?

4个回答

9

JVM并非支持所有编程语言,有些语言在其上不能高效实现。例如,JVM不提供内存寻址操作,因此一个低层次的语言如C在JVM上的实现将会非常低效。但是其原始操作的集合足以支持许多不同于Java的流行语言,只要有一个足够聪明的编译器。可以被良好实现的语言并不一定只是带语法糖的Java;但是当然,你离Java越远,实现这个语言就越难。


1
只要这些“流行语言”在语义上等同于Java,就可以。 - SK-logic
1
@SK:以克林顿的方式来说,我必须得说这得看你对“语义上等价”的定义。Java 只有静态类型;Ruby 有“鸭子类型”。但是 JRuby 存在。 - Ernest Friedman-Hill
2
Java仍然是一种命令式的、面向对象的语言。JRuby具有类似的程序流程。在对象上调用带参数的方法并重复执行。在Java虚拟机上实现类似Haskell或Erlang这样的东西可能会有困难,除非使用hack技巧。 - Berlin Brown
1
@Ernest,这只是一个小差别,但JRUBY编译器确实非常复杂。JVM不允许您高效地编译(而且Ruby从来没有被设计用于效率)任何与Java显著不同的语言。两个主要问题是:缺乏显式尾调用和荒谬的方法大小限制。 - SK-logic
2
如果你想的话,可以用Java写一个解释器来解释另一种语言。这就是为什么JVM中有许多语言的原因,特别是当该语言对计算机做出了不适用于JVM的假设时。由于所有图灵完备的语言都具有(可证明的)相等的(表达)能力,因此每种图灵完备的语言都应该存在一种Rube Goldberg解决方案来模拟另一种图灵完备语言的操作。 - Edwin Buck
显示剩余20条评论

4
JVM并非无关语言,但它的语言是JVM字节码。将其视为虚拟机的汇编代码,你就能了解JVM的运行方式了。JVM字节码被选为Java程序运行的途径,但与任何"完整的"汇编语言一样,它也可用于其他许多用途。关键在于编译过程中所做的工作。
JVM的其他语言障碍包括其设计为基于堆栈的机器,这意味着在字节码层面上,显式地址对JVM来说是无意义的。没有"load"或"store"操作;然而,这并不妨碍那些想要在JVM上实现地址引用语言的人。这只是使那些想要进行寻址的人更加困难。
要在JVM上进行寻址,你基本上需要编写一个模拟器,其中包含"地址到对象句柄"查找表的对象。这允许你通过在缺乏寻址功能的虚拟机上进行模拟来进行基本的寻址。虽然不总是美观的,但模拟的质量通常仅限于所模拟语言允许的用例。
是的,通过对象地址、Map(对象到Java堆引用)和在JVM内部进行的Java堆引用到物理内存地址的转换,你会失去一点性能。但这是为了保持平台无关而必须做的。如果你直接访问内存,你最终会被推到为不同的硬件平台编写代码,而不是为虚拟机编写代码。至少,你将比现在更早地被推向特定于平台的代码。

如果在支持32位整数的64位内存寻址模型上编写JVM呢?那其他系统呢?放弃C语言的类型检查,而采用地址的int打包,这种做法最好的计划边缘有些荒谬。使用void(或更好的是struct)有什么问题呢?它们的大小可能不知道,但你不必违背类型系统。 - Edwin Buck
JVM的C编译器可以生成代码,将一个或多个数组的引用存储在“静态”变量中,然后使用这些数组来保存所有地址被获取的内容。编译器将把使用结构体等内容的代码转换为JVM代码,为那些地址未被获取的内容生成适当的数组访问(地址从未被获取的结构体可以简单地被视为一堆独立的变量)。由于JVM没有非基本值类型的概念,任何类型的结构体数组行为都必须被模拟。 - supercat
你应该意识到JVM是用C语言编写的。你所有的C# / Java示例都不涉及JVM内部工作方式,而且建议在C语言中使用int数组来保存内存地址是非常糟糕的做法,无论其他语言有什么特性。 - Edwin Buck
我建议如何编写一个C编译器,它可以接受可移植C程序的源代码,并生成一个字节码文件,其在JVM上的行为与在符合ANSI标准的编译器上运行可移植C程序的行为相匹配。请注意,在C中,可以使用指针以100%保证可移植的方式使用,但无法静态验证永远不会调用未定义的行为。只有通过使用数组作为后备存储器,编译器才能匹配C代码的标准规定行为。 - supercat
关于“为什么要使用 int pack”,如果编译器试图生成与使用 char* 修改 int 部分的 C 代码相当的 JVM 兼容代码,而 C 编译器将 CHAR_MAX 定义为 255,将 UINT_MAX 定义为 4294967295,那么除了使用多个 byte[] 元素来存储每个 C int,或者使用每个 int[] 元素来保存多个 C char 的方法外,还有哪些替代方案呢?我认为,如果 C 实现规定所有 int 指针都必须是长字对齐的,那么 int-packing 会更快。 - supercat
显示剩余8条评论

4
就 JVM 和 Java 字节码是图灵完备的这一点而言,任何其他图灵完备语言都可以转换并编译为 Java 字节码,在 JVM 上运行。虽然可能效率非常低下,但并非不可能。至于“不可知”的最严格定义,不存在这样的事情。在硬件层面上,所有处理器都有一组二进制指令集支持,因此任何语言都必须被转换为与其要执行的硬件兼容的汇编语言。
编辑: JVM 的开发不是孤立进行的,它是与 JAVA 编程语言一起开发的,因此可以说 Java 语言在 Java 字节码和 JVM 的设计中产生了很大影响。因此,从这个意义上说,可以说 JVM 是针对 Java 设计的。但也确实如此,在架构上,JVM 被有意地与 Java 语言解耦(通过中间字节码格式),因此设计中考虑到了可能的替代语言。

我的意思是,JVM 是否针对 Java 进行了专门的设计? - Berlin Brown
是的,它们都是图灵完备的,并且在某种效率水平上,可以用来模拟其他语言。 - Edwin Buck
1
@BerlinBrown JVM不是孤立开发的,它是与JAVA编程语言一起开发的,因此可以说Java语言严重影响了Java字节码和JVM的设计。因此,在这个意义上,可以说JVM是针对Java设计的。但同样也正确的是,在体系结构方面,JVM有意识地与Java语言解耦(通过中间字节码格式),因此在设计中考虑了可能的替代语言元素。 - pap
很棒,这是一个好的回答。你可能想把它加入到你的答案中。 - Berlin Brown

2

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接