Java - 二进制代码与字节码相同吗?
在Java中,“二进制代码”是否与“Java字节码”的含义相同?
这是Java中的流程吗?
Java File (.java) -> [javac] -> ByteCode File (.class) -> [JVM/Java Interpreter] -> Running it(首先将其转换为特定于机器的二进制代码)
谢谢!
在Java中,“二进制代码”是否与“Java字节码”的含义相同?
这是Java中的流程吗?
Java File (.java) -> [javac] -> ByteCode File (.class) -> [JVM/Java Interpreter] -> Running it(首先将其转换为特定于机器的二进制代码)
谢谢!
答案取决于您所说的二进制代码
是什么意思。
Java 字节码
是一种二进制数据格式,包括 Java 虚拟机的加载信息和执行指令。从这个意义上说,Java字节码
是一种特殊的二进制代码。
当您使用术语“二进制代码”来表示实际处理器架构(如IA-32或Sparc)的机器指令时,它就不同了。
从这个意义上说,Java字节码
不是二进制代码。它不是特定于处理器的。
JVM是非常复杂的程序,并且那里的流程在一定水平上是不可预测的。例如,HotSpot JVM 内部的流程如下所示:
1)它采用您的字节码并对其进行解释
2)如果某个方法执行得非常频繁(在某些时间跨度中执行了一些次数),它将被标记为“热”方法,JVM将其编译调度到依赖于平台的机器代码(这就是您所说的二进制代码吗?)。该流如下所示:
ByteCode
--> Hige-level Intermediate Representation (HIR)
--> Middle-level Intermediate Representation (MIR)
--> Low-level Intermediate Representation (LIR)
--> Register Allocation
--> EMIT (platform dependent machine code)
该流程中的每个步骤都很重要,可帮助 JVM 对代码执行一些优化。当然,它不会改变你的算法,优化只是意味着可以检测一些代码序列并与性能更好的代码交换(产生相同的结果)。从 LIR 阶段开始,代码变得依赖于平台(!)。
字节码可以很好地解释,但不足以轻松转换为机器本机代码。HIR负责它,其目的是将字节码快速转换为中间表示。MIR 将所有操作转换为三操作数操作;字节码基于堆栈操作:
iload_0
iload_1
iand
这是简单操作的字节码,而其中级表示将是以下内容:and
and v0 v1 -> v2
LIR依赖于平台,考虑到我们的操作简单示例,并将我们的平台指定为x86,那么我们的代码片段将是:and
x86_and v1 v0 -> v1
x86_move v1 -> v2
因为操作需要两个操作数,第一个是目标,另一个是源,然后我们将结果值放在另一个“变量”中。下一阶段是“寄存器分配”,因为x86平台(可能还有大多数其他平台)使用寄存器,而不是变量(如中间表示),也不使用堆栈(如字节码)。在这里,我们的代码片段应该如下所示:and
x86_and eax ecx -> eax
在这里,您可以注意到缺少“移动”操作。我们的代码只包含一行,JVM发现不需要创建新的虚拟变量;我们可以重用寄存器。如果代码足够大,有许多变量并且使用它们很密集(例如,在下面的某个地方使用eax,因此我们无法更改其值),那么您将看到机器代码中留下的移动操作。这又是关于优化:)eax
这是JIT流,但根据VM实现,可以再有一个步骤 - 如果代码被编译(“热”),并且仍然执行了很多次,JVM调度该代码的优化(例如使用内联)。
好吧,结论是,从字节码到机器码的路径非常有趣,有点不可预见,并且取决于许多事情。
顺便说一句,上面描述的过程称为“混合模式解释”(当JVM首先解释字节码,然后使用JIT编译时),这种JVM的例子是HotSpot。一些JVM(如Oracle的JRockit)仅使用JIT编译。
这是对那里正在发生的事情的非常简单的描述。我希望它有助于在非常高的层次上理解JVM内部的流程,并针对有关字节码和二进制代码之间差异的问题。有关此处未提及的以及与该主题相关的其他问题的参考,请阅读类似的主题“为什么编译的 Java 类文件小于 C 编译的文件?
也请随时批评这个答案,指出我的错误或误解,我总是愿意提高我对JVM:)