|
|
Optimize with a SATA RAID Storage Solution
Range of capacities as low as $1250 per TB. Ideal if you currently rely on servers/disks/JBODs
Page 2 of 2
| Opcode | Operand(s) | Description |
|---|
lookupswitch |
<0-3 byte pad>defaultbyte1, defaultbyte2, defaultbyte3, defaultbyte4, npairs1, npairs2, npairs3, npairs4, case value/branch offset pairs... | pop key, match key with case values, if match found jump to associated branch offset, else jump to default branch offset |
tableswitch |
<0-3 byte pad>defaultbyte1, defaultbyte2, defaultbyte3, defaultbyte4, lowbyte1, lowbyte2, lowbyte3, lowbyte4, highbyte1, highbyte2, highbyte3, highbyte4, branch offsets... | pop key, if not in low/high range jump to default branch offset, else get the (key - low) branch offset and jump |
Other than the opcodes described above, the only Java virtual machine instructions that affect control flow are those that deal with throwing and catching exceptions, try-finally clauses, and invoking and returning from methods. The bytecodes for exceptions and try-finally clauses were discussed in the previous two installments of this column (see Resources). The bytecodes that deal with invoking and returning from methods will be treated in a future installment.
The applet below demonstrates a Java virtual machine executing a sequence of bytecodes. The bytecode sequence in the simulation
was generated by the javac compiler for the argue() method of the class shown below:
class Argument {
public final static int TOMAYTO = 0;
public final static int TOMAHTO = 1;
static void argue() {
int say = TOMAYTO;
while (true) {
switch (say) {
case TOMAYTO:
say = TOMAHTO;
break;
case TOMAHTO:
say = TOMAYTO;
break;
}
}
}
}
The bytecodes generated by javac for the argue() method are shown below:
0 iconst_0 // Push constant 0 (TOMAYTO)
1 istore_0 // Pop into local var 0: int say = TOMAYTO;
2 iload_0 // Push key for switch from local var 0
// Perform switch statement: switch (say) {...
// Low case value is 0, high case value is 1
// Default branch offset will goto 2
3 tableswitch 0 to 1: default=2
0: 24 // case 0 (TOMAYTO): goto 24
1: 29 // case 1 (TOMAHTO): goto 29
// Note that the next instruction starts at address 24,
// which means that the tableswitch took up 21 bytes
24 iconst_1 // Push constant 1 (TOMAHTO)
25 istore_0 // Pop into local var 0: say = TOMAHTO
26 goto 2 // Branch unconditionally to 2, top of while loop
29 iconst_0 // Push constant 1 (TOMAYTO)
30 istore_0 // Pop into local var 0: say = TOMAYTO
31 goto 2 // Branch unconditionally to 2, top of while loop
The argue() method merely switches the value of say back and forth between TOMAYTO and TOMAHTO. Because the values of TOMAYTO and TOMAHTO were consecutive (TOMAYTO was a 0 and TOMAHTO was a 1), the javac compiler used a tableswitch. The tableswitch is a more efficient instruction than a lookupswitch, and the equivalent lookupswitch instruction would occupy 28 bytes -- 4 bytes more than the tableswitch instruction.
It turns out that even if TOMAYTO were a 0 and TOMAHTO were a 2, the javac compiler still would have used a tableswitch, because even with the extra default branch offset in there for a 1, the tableswitch instruction would occupy only 28 bytes -- the same number of bytes as the equivalent lookupswitch. Both instructions occupy the same number of bytes, but tableswitch is more efficient, so it is used. As soon as you make TOMAHTO a 3, however, javac starts using a lookupswitch. This is because a tableswitch now would need two default branch offsets in its list (for 1 and 2), which would push its size up to 32 bytes. Thus, a lookupswitch now would require fewer bytes than a tableswitch -- so javac would choose the lookupswitch.
The branch offsets for the case values cause the Java virtual machine to hop down to code that will change the value of the
say local variable. The value of say will alternate between TOMAYTO and TOMAHTO indefinitely, until the user aborts the program, thereby calling the whole thing off.
To drive the simulation, just press the Step button. Each press of the Step button will cause the Java virtual machine to execute one bytecode instruction. To start the simulation over, press the Reset button. To cause the JVM to repeatedly execute bytecodes with no further coaxing on your part, press the Run button. The JVM will then execute the bytecodes until the Stop button is pressed. The text area at the bottom of the applet describes the next instruction to be executed. Happy clicking.