jvm运行时数据区为每个正在执行的方法提供了独立的堆栈。它包含操作数堆栈和局部变量。每次加载变量时,都需要 const 到操作数堆栈,然后 store 到局部变量。为什么不直接操作局部变量表,而进行一些看似重复的工作呢?
const
store
nukf8bse1#
具有直接操作数的指令集必须对每条指令中的操作数进行编码。相反,对于使用操作数堆栈的指令集,操作数是隐式的。隐式参数的优势并不明显,当看一个小的琐碎的操作,如加载一个常数到一个变量。这个例子将“opcode,constant,opcode,variable index”序列与“opcode,constant,variable index”序列进行比较,因此直接寻址似乎更简单、更紧凑。但是让我们看看,例如。 return Math.sqrt(a * a + b * b); 假设变量索引从零开始,字节码看起来像
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_n , bipush , sipush ,和 ldc 为了将它存储到变量中 istore_n , istore n ,和 wide istore n . 具有直接变量寻址的指令集需要为每个组合提供不同的指令,因为它应该支持范围广泛的常量和变量数,但仍然支持紧凑指令。同样,它也需要所有算术指令的多个版本。可以使用两个操作数形式,其中一个源变量还指示目标变量,而不是三个操作数形式。这会产生更紧凑的指令,但如果之后仍然需要操作数的值,则需要额外的传输指令。操作数堆栈形式仍然更紧凑。请记住,这只描述了操作。执行代码时,执行环境不需要严格遵循此逻辑。因此,除了最简单的解释器之外,所有jvm实现在执行之前都会将其转换为不同的形式,因此原始存储形式与实际执行性能无关。它只影响空间需求和加载时间,这两个都得益于更紧凑的表示。这尤其适用于通过可能较慢的网络连接传输的代码,这是java最初设计的用例之一。
iconst_n
bipush
sipush
ldc
istore_n
istore n
wide istore n
1条答案
按热度按时间nukf8bse1#
具有直接操作数的指令集必须对每条指令中的操作数进行编码。相反,对于使用操作数堆栈的指令集,操作数是隐式的。
隐式参数的优势并不明显,当看一个小的琐碎的操作,如加载一个常数到一个变量。这个例子将“opcode,constant,opcode,variable index”序列与“opcode,constant,variable index”序列进行比较,因此直接寻址似乎更简单、更紧凑。
但是让我们看看,例如。
return Math.sqrt(a * a + b * b);
假设变量索引从零开始,字节码看起来像对于直接寻址体系结构,我们需要
我们必须用索引替换名称。
虽然此序列由较少的指令组成,但每条指令都必须对其操作数进行编码。当我们希望能够寻址256个局部变量时,每个操作数需要一个字节,因此每个算术指令需要三个字节加上操作码,调用需要两个字节加上操作码和方法地址,返回需要一个字节加上操作码。因此,对于字节边界的指令,这个序列需要19个字节,远远超过等效的java字节码,同时被限制为256个局部变量,而字节码最多支持65536个局部变量。
这说明了操作数堆栈概念的另一个优点。java字节码允许组合不同的优化指令,例如,加载整数常量时
iconst_n
,bipush
,sipush
,和ldc
为了将它存储到变量中istore_n
,istore n
,和wide istore n
. 具有直接变量寻址的指令集需要为每个组合提供不同的指令,因为它应该支持范围广泛的常量和变量数,但仍然支持紧凑指令。同样,它也需要所有算术指令的多个版本。可以使用两个操作数形式,其中一个源变量还指示目标变量,而不是三个操作数形式。这会产生更紧凑的指令,但如果之后仍然需要操作数的值,则需要额外的传输指令。操作数堆栈形式仍然更紧凑。
请记住,这只描述了操作。执行代码时,执行环境不需要严格遵循此逻辑。因此,除了最简单的解释器之外,所有jvm实现在执行之前都会将其转换为不同的形式,因此原始存储形式与实际执行性能无关。它只影响空间需求和加载时间,这两个都得益于更紧凑的表示。这尤其适用于通过可能较慢的网络连接传输的代码,这是java最初设计的用例之一。