Java 操作数堆栈在JVM中的作用是什么?

Java 操作数堆栈在JVM中的作用是什么?,java,jvm,bytecode,jvm-bytecode,Java,Jvm,Bytecode,Jvm Bytecode,JVM运行时数据区域为正在执行的每个方法提供单独的堆栈。它包含操作数堆栈和局部变量。每次加载变量时,都需要const到操作数堆栈,然后存储到局部变量。为什么不直接操作局部变量表,并进行一些看似重复的工作?具有直接操作数的指令集必须对每条指令中的操作数进行编码。相反,对于使用操作数堆栈的指令集,操作数是隐式的 隐式参数的优势在处理诸如将常数加载到变量中这样的小的琐碎操作时并不明显。本例将“操作码,常量,操作码,变量索引”序列与“操作码,常量,变量索引”序列进行比较,因此直接寻址似乎更简单、更紧凑

JVM运行时数据区域为正在执行的每个方法提供单独的堆栈。它包含操作数堆栈和局部变量。每次加载变量时,都需要
const
到操作数堆栈,然后
存储到局部变量。为什么不直接操作局部变量表,并进行一些看似重复的工作?

具有直接操作数的指令集必须对每条指令中的操作数进行编码。相反,对于使用操作数堆栈的指令集,操作数是隐式的

隐式参数的优势在处理诸如将常数加载到变量中这样的小的琐碎操作时并不明显。本例将“操作码,常量,操作码,变量索引”序列与“操作码,常量,变量索引”序列进行比较,因此直接寻址似乎更简单、更紧凑

但是让我们看看,例如,
return Math.sqrt(a*a+b*b)

假设变量索引从零开始,字节码如下

   0: dload_0
   1: dload_0
   2: dmul
   3: dload_2
   4: dload_2
   5: dmul
   6: dadd
   7: invokestatic  #2                  // Method java/lang/Math.sqrt:(D)D
  10: dreturn
  11 bytes total
对于直接寻址体系结构,我们需要

dmul a,a → tmp1
dmul b,b → tmp2
dadd tmp1,tmp2 → tmp1
invokestatic #2 tmp1 → tmp1
dreturn tmp1
我们必须用索引替换名称

虽然该序列由较少的指令组成,但每条指令都必须对其操作数进行编码。当我们希望能够寻址256个局部变量时,每个操作数需要一个字节,因此每个算术指令需要三个字节加上操作码,调用需要两个字节加上操作码和方法地址,返回需要一个字节加上操作码。因此,对于字节边界的指令,该序列需要19个字节,远远超过等效的Java字节码,同时限制为256个局部变量,而字节码最多支持65536个局部变量

这说明了操作数堆栈概念的另一个优点。Java字节码允许组合不同的优化指令,例如,对于加载整数常量,有
iconst\u n
bipush
sipush
ldc
,对于将其存储到变量中,有
istore n
istore n
wide-istore n
。具有直接变量寻址的指令集在支持大量常量和变量但仍支持紧凑指令的情况下,每种组合都需要不同的指令。同样,它也需要所有算术指令的多个版本

可以使用两个操作数形式,而不是三个操作数形式,其中一个源变量还指示目标变量。这会产生更紧凑的指令,但如果之后仍然需要操作数的值,则需要额外的传输指令。操作数堆栈形式更加紧凑


请记住,这只描述了操作。执行代码时,执行环境不需要严格遵循此逻辑。因此,除了最简单的解释器外,所有JVM实现都会在执行之前将其转换为不同的形式,因此原始存储形式与实际执行性能无关。它只影响空间需求和加载时间,这两者都得益于更紧凑的表示。这尤其适用于通过可能较慢的网络连接传输的代码,这是Java最初设计的用例之一。

因为堆栈机器就是这样工作的。使实现(编译器和解释器)比处理基于寄存器的系统简单得多,而且编码也非常紧凑。这对高效口译也很不利,但在90年代,这并不是什么大问题。但我担心这是一个过于宽泛和基于观点的问题。这篇维基百科文章乍一看似乎很合理,但可能是一个好的开端。如果你对编译器感兴趣,《龙之书》将是一个不错的选择。“
const
到操作数堆栈”是什么意思?你是说加载指令吗?这是一个基于堆栈的体系结构:这就是它们的工作原理。@jiangyongbing24有什么理由不接受这个答案吗?