Welcome to ShenZhenJia Knowledge Sharing Community for programmer and developer-Open, Learning and Share
menu search
person
Welcome To Ask or Share your Answers For Others

Categories

JVM Run-time Data Areas separate stack for each method being executed. It contains operand stack and local variables. Every time you load a variable, you need to const to the operand stack and then store to the local variables. Why not directly operate the local variable table, and have some seemingly repeated work?

See Question&Answers more detail:os

与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
thumb_up_alt 0 like thumb_down_alt 0 dislike
129 views
Welcome To Ask or Share your Answers For Others

1 Answer

An instruction set with direct operands has to encode the operands in each instruction. In contrast, with an instruction set using an operand stack, the operands are implicit.

The advantage of implicit arguments is not obvious when looking at a small trivial operation like loading a constant into a variable. This example is comparing an “opcode, constant, opcode, variable-index” sequence with “opcode, constant, variable index”, so it seems like addressing directly is simpler and more compact.

But let’s look at, e.g. return Math.sqrt(a * a + b * b);

Assuming the variable indices start at zero, the bytecode looks like

   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

For a directly addressing architecture, we would need something like

dmul a,a → tmp1
dmul b,b → tmp2
dadd tmp1,tmp2 → tmp1
invokestatic #2 tmp1 → tmp1
dreturn tmp1

where we have to replace the names with indices.

While this sequence consists of fewer instructions, each instruction has to encode its operands. When we want to be able to address 256 local variables, we need a byte per operand, so each arithmetic instruction needs three bytes plus opcode, the invocation needs two plus opcode and method address, and the return needs one plus opcode. So for instructions at byte boundaries, this sequence needs 19 bytes, significantly more than the equivalent Java bytecode, while being limited to 256 local variables whereas the bytecode supports up to 65536 local variables.

This demonstrates another strength of the operand stack concept. Java bytecode allows to combine different, optimized instructions, e.g. for loading an integer constant there are iconst_n, bipush, sipush, and ldc and for storing it into a variable there are istore_n, istore n, and wide istore n. An instruction set with direct variable addressing would need distinct instructions for each combination when it is supposed to support a wide range of constants and numbers of variables but still support compact instructions. Likewise, it would need multiple versions of all arithmetic instructions then.

Instead of a three operand form, you could use a two operand form, where one of the source variables also indicates the target variable. This results in more compact instructions but creates the need for additional transfer instructions if the operand’s value is still needed afterwards. The operand stack form still is more compact.

Keep in mind that this only describes the operations. An execution environment is not required to strictly follow this logic when executing the code. So besides the simplest interpreters, all JVM implementations convert this into a different form before executing, so the original stored form doesn’t matter for the actual execution performance. It only affects the space requirements and loading time, which both benefit from a more compact representation. This especially applies to code transferred over potentially slow network connections, one of the use cases, Java was originally designed for.


与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
thumb_up_alt 0 like thumb_down_alt 0 dislike
Welcome to ShenZhenJia Knowledge Sharing Community for programmer and developer-Open, Learning and Share
...