字节码
enum Code { _illegal = -1, // Java bytecodes _nop = 0, // 0x00 _aconst_null = 1, // 0x01 _iconst_m1 = 2, // 0x02 _iconst_0 = 3, // 0x03 _iconst_1 = 4, // 0x04 _iconst_2 = 5, // 0x05 _iconst_3 = 6, // 0x06 _iconst_4 = 7, // 0x07 _iconst_5 = 8, // 0x08 _lconst_0 = 9, // 0x09 _lconst_1 = 10, // 0x0a _fconst_0 = 11, // 0x0b _fconst_1 = 12, // 0x0c _fconst_2 = 13, // 0x0d _dconst_0 = 14, // 0x0e _dconst_1 = 15, // 0x0f _bipush = 16, // 0x10 _sipush = 17, // 0x11 _ldc = 18, // 0x12 _ldc_w = 19, // 0x13 _ldc2_w = 20, // 0x14 _iload = 21, // 0x15 _lload = 22, // 0x16 _fload = 23, // 0x17 _dload = 24, // 0x18 _aload = 25, // 0x19 _iload_0 = 26, // 0x1a _iload_1 = 27, // 0x1b _iload_2 = 28, // 0x1c _iload_3 = 29, // 0x1d _lload_0 = 30, // 0x1e _lload_1 = 31, // 0x1f _lload_2 = 32, // 0x20 _lload_3 = 33, // 0x21 _fload_0 = 34, // 0x22 _fload_1 = 35, // 0x23 _fload_2 = 36, // 0x24 _fload_3 = 37, // 0x25 _dload_0 = 38, // 0x26 _dload_1 = 39, // 0x27 _dload_2 = 40, // 0x28 _dload_3 = 41, // 0x29 _aload_0 = 42, // 0x2a _aload_1 = 43, // 0x2b _aload_2 = 44, // 0x2c _aload_3 = 45, // 0x2d _iaload = 46, // 0x2e _laload = 47, // 0x2f _faload = 48, // 0x30 _daload = 49, // 0x31 _aaload = 50, // 0x32 _baload = 51, // 0x33 _caload = 52, // 0x34 _saload = 53, // 0x35 _istore = 54, // 0x36 _lstore = 55, // 0x37 _fstore = 56, // 0x38 _dstore = 57, // 0x39 _astore = 58, // 0x3a _istore_0 = 59, // 0x3b _istore_1 = 60, // 0x3c _istore_2 = 61, // 0x3d _istore_3 = 62, // 0x3e _lstore_0 = 63, // 0x3f _lstore_1 = 64, // 0x40 _lstore_2 = 65, // 0x41 _lstore_3 = 66, // 0x42 _fstore_0 = 67, // 0x43 _fstore_1 = 68, // 0x44 _fstore_2 = 69, // 0x45 _fstore_3 = 70, // 0x46 _dstore_0 = 71, // 0x47 _dstore_1 = 72, // 0x48 _dstore_2 = 73, // 0x49 _dstore_3 = 74, // 0x4a _astore_0 = 75, // 0x4b _astore_1 = 76, // 0x4c _astore_2 = 77, // 0x4d _astore_3 = 78, // 0x4e _iastore = 79, // 0x4f _lastore = 80, // 0x50 _fastore = 81, // 0x51 _dastore = 82, // 0x52 _aastore = 83, // 0x53 _bastore = 84, // 0x54 _castore = 85, // 0x55 _sastore = 86, // 0x56 _pop = 87, // 0x57 _pop2 = 88, // 0x58 _dup = 89, // 0x59 _dup_x1 = 90, // 0x5a _dup_x2 = 91, // 0x5b _dup2 = 92, // 0x5c _dup2_x1 = 93, // 0x5d _dup2_x2 = 94, // 0x5e _swap = 95, // 0x5f _iadd = 96, // 0x60 _ladd = 97, // 0x61 _fadd = 98, // 0x62 _dadd = 99, // 0x63 _isub = 100, // 0x64 _lsub = 101, // 0x65 _fsub = 102, // 0x66 _dsub = 103, // 0x67 _imul = 104, // 0x68 _lmul = 105, // 0x69 _fmul = 106, // 0x6a _dmul = 107, // 0x6b _idiv = 108, // 0x6c _ldiv = 109, // 0x6d _fdiv = 110, // 0x6e _ddiv = 111, // 0x6f _irem = 112, // 0x70 _lrem = 113, // 0x71 _frem = 114, // 0x72 _drem = 115, // 0x73 _ineg = 116, // 0x74 _lneg = 117, // 0x75 _fneg = 118, // 0x76 _dneg = 119, // 0x77 _ishl = 120, // 0x78 _lshl = 121, // 0x79 _ishr = 122, // 0x7a _lshr = 123, // 0x7b _iushr = 124, // 0x7c _lushr = 125, // 0x7d _iand = 126, // 0x7e _land = 127, // 0x7f _ior = 128, // 0x80 _lor = 129, // 0x81 _ixor = 130, // 0x82 _lxor = 131, // 0x83 _iinc = 132, // 0x84 _i2l = 133, // 0x85 _i2f = 134, // 0x86 _i2d = 135, // 0x87 _l2i = 136, // 0x88 _l2f = 137, // 0x89 _l2d = 138, // 0x8a _f2i = 139, // 0x8b _f2l = 140, // 0x8c _f2d = 141, // 0x8d _d2i = 142, // 0x8e _d2l = 143, // 0x8f _d2f = 144, // 0x90 _i2b = 145, // 0x91 _i2c = 146, // 0x92 _i2s = 147, // 0x93 _lcmp = 148, // 0x94 _fcmpl = 149, // 0x95 _fcmpg = 150, // 0x96 _dcmpl = 151, // 0x97 _dcmpg = 152, // 0x98 _ifeq = 153, // 0x99 _ifne = 154, // 0x9a _iflt = 155, // 0x9b _ifge = 156, // 0x9c _ifgt = 157, // 0x9d _ifle = 158, // 0x9e _if_icmpeq = 159, // 0x9f _if_icmpne = 160, // 0xa0 _if_icmplt = 161, // 0xa1 _if_icmpge = 162, // 0xa2 _if_icmpgt = 163, // 0xa3 _if_icmple = 164, // 0xa4 _if_acmpeq = 165, // 0xa5 _if_acmpne = 166, // 0xa6 _goto = 167, // 0xa7 _jsr = 168, // 0xa8 _ret = 169, // 0xa9 _tableswitch = 170, // 0xaa _lookupswitch = 171, // 0xab _ireturn = 172, // 0xac _lreturn = 173, // 0xad _freturn = 174, // 0xae _dreturn = 175, // 0xaf _areturn = 176, // 0xb0 _return = 177, // 0xb1 _getstatic = 178, // 0xb2 _putstatic = 179, // 0xb3 _getfield = 180, // 0xb4 _putfield = 181, // 0xb5 _invokevirtual = 182, // 0xb6 _invokespecial = 183, // 0xb7 _invokestatic = 184, // 0xb8 _invokeinterface = 185, // 0xb9 _invokedynamic = 186, // 0xba // if EnableInvokeDynamic _new = 187, // 0xbb _newarray = 188, // 0xbc _anewarray = 189, // 0xbd _arraylength = 190, // 0xbe _athrow = 191, // 0xbf _checkcast = 192, // 0xc0 _instanceof = 193, // 0xc1 _monitorenter = 194, // 0xc2 _monitorexit = 195, // 0xc3 _wide = 196, // 0xc4 _multianewarray = 197, // 0xc5 _ifnull = 198, // 0xc6 _ifnonnull = 199, // 0xc7 _goto_w = 200, // 0xc8 _jsr_w = 201, // 0xc9 _breakpoint = 202, // 0xca number_of_java_codes, // JVM bytecodes _fast_agetfield = number_of_java_codes, _fast_bgetfield , _fast_cgetfield , _fast_dgetfield , _fast_fgetfield , _fast_igetfield , _fast_lgetfield , _fast_sgetfield , _fast_aputfield , _fast_bputfield , _fast_zputfield , _fast_cputfield , _fast_dputfield , _fast_fputfield , _fast_iputfield , _fast_lputfield , _fast_sputfield , _fast_aload_0 , _fast_iaccess_0 , _fast_aaccess_0 , _fast_faccess_0 , _fast_iload , _fast_iload2 , _fast_icaload , _fast_invokevfinal , _fast_linearswitch , _fast_binaryswitch , // special handling of oop constants: _fast_aldc , _fast_aldc_w , _return_register_finalizer , _shouldnotreachhere, // For debugging // Platform specific JVM bytecodes #ifdef TARGET_ARCH_x86 # include "bytecodes_x86.hpp" #endif #ifdef TARGET_ARCH_sparc # include "bytecodes_sparc.hpp" #endif #ifdef TARGET_ARCH_zero # include "bytecodes_zero.hpp" #endif #ifdef TARGET_ARCH_arm # include "bytecodes_arm.hpp" #endif #ifdef TARGET_ARCH_ppc # include "bytecodes_ppc.hpp" #endif number_of_codes };
将操作栈中的栈顶元素弹出并设值本地变量表对应的变量。
astore
String s = "greet";
0: ldc #2 // String greet
2: astore_1
astore_<n>
指令_astore,_astore_0,_astore_1,_astore_2,_astore_3
dstore
dstore_<n>
fstore
fstore_<n>
istore
istore_<n>
lstore
lstore_<n>
int i = 10; long l = 100L; float f = 0.1F; double d = 0.1; String s = "abc"; char c = 'a';0: bipush 10
2: istore_1
3: ldc2_w #2 // long 100l
6: lstore_2
7: ldc #4 // float 0.1f
9: fstore 4
11: ldc2_w #5 // double 0.1d
14: dstore 5
16: ldc #7 // String abc
18: astore 7
20: bipush 97
22: istore 8
bastore
castore
dastore
fastore
iastore
lastore
sastore
将本地变量表中对应的变量压入到操作栈中。
aload
String s = "greet";
0: ldc #2 // String greet
2: astore_1
String s2 = s;
3: aload_1
4: astore_2
aload_<n>
指令_aload_0,_aload_1,_aload_2,_aload_3,_fast_aload_0
dload
dload_<n>
fload
fload_<n>
iload
iload_<n>
lload
lload_<n>
aaload
baload
caload
daload
faload
iaload
laload
saload
int i2 = i; long l2 = l; float f2 = f; double d2 = d; String s2 = s; char c2 = c;
24: iload_1
25: istore 9
27: lload_2
28: lstore 10
30: fload 4
32: fstore 12
34: dload 5
36: dstore 13
38: aload 7
40: astore 15
42: iload 8
44: istore 16
putfield
假设对象实例i有个字段value,对value赋值如下:
private int value;
value = 1;
0: aload_0
1: iconst_1
2: putfield #2 // Field value:I
getfield
假设对象实例i有个字段value,对value赋值如下:
private int value;
value++;
0: aload_0
1: dup
2: getfield #2 // Field value:I
5: iconst_1
6: iadd
7: putfield #2 // Field value:I
int value2 = value;
0: aload_0
1: getfield #2 // Field value:I
4: istore_1
putstatic
getstatic
new
格式:new indexbyte1 indexbyte2
在Heap上为指定的类对象先分配一块内存,并初始化其中的变量为默认的初始值,最后将该对象的一个引用objectref压入操作栈的栈顶。
这里的类由new字节码随后的indexbyte1,indexbyte2两个参数决:(indexbyte1 << 8) | indexbyte2,其值表示要实例化的类在常量池中的索引
创建对象new例子:
public class JavaNew { public static void main(String[] args) { Object obj = new Object(); } }反汇编结果:
>javap -c JavaNew
Compiled from "JavaNew.java"
public class JavaNew {
public JavaNew();
Code:
0: aload_0
1: invokespecial #1 // Method java/lang/Object."<init>":
()V
4: return
public static void main(java.lang.String[]);
Code:
0: new #2 // class java/lang/Object
3: dup
4: invokespecial #1 // Method java/lang/Object."<init>":
()V
7: astore_1
8: return
}
从反编译的结果看,生成了一个构造方法,源代码中我没有为JavaNew定义任何构造方法,如果没有显式定义构造方法,Java会隐式定义一个默认的无参构造方法。在public JavaNew();下面是这个构造方法的字节码实现指令,其中一条重要的指令:invokespecial #1,在JVM解释执行遇到这个指令的时候,JVM会调用一个特殊方法:<init>,这从后面的注释也可以看出来:// Method java/lang/Object."<init>":()V,它对应Java层面的构造方法:public JavaNew() {},在这里我们可以认为构造方法也是一个特殊的构造方法,特殊在于在Java层面上它的方法名在Java层面看和类型一样,但在JVM层面却对应的是:<init>,另外没有显式的返回类型,只有参数类型是一样的。最后要注意的事后面的()V,它是构造方法的签名,其形式如下:
(参数类型签名列表)返回类型签名
在另一篇文章:https://lobin.iteye.com/blog/2437928,讲GetMethodID函数的时候也会传入对应方法的签名。
创建数组newarray例子:
newarray
public class JavaNewArray { public static void main(String[] args) { int test[] = new int[123]; } }反汇编结果:
>javap -c JavaNewArray Compiled from "JavaNewArray.java" public class JavaNewArray { public JavaNewArray(); Code: 0: aload_0 1: invokespecial #1 // Method java/lang/Object."<init>": ()V 4: return public static void main(java.lang.String[]); Code: 0: bipush 123 2: newarray int 4: astore_1 5: return }
dup
拷贝操作栈顶元素(值),并将拷贝的值压入到操作栈成为新的栈顶。
调用指令
invokedynamic
Java7开始支持invokedynamic指令。
public class MainKlass5 { public static void main(String[] args) { Runnable x = () -> { System.out.println("greet!"); }; x.run(); new Thread(x).start(); } }
public static void main(java.lang.String[]);
Code:
0: invokedynamic #2, 0 // InvokeDynamic #0:run:()Ljava/lang/Runnable;
5: astore_1
6: aload_1
7: invokeinterface #3, 1 // InterfaceMethod java/lang/Runnable.run:()V
12: new #4 // class java/lang/Thread
15: dup
16: aload_1
17: invokespecial #5 // Method java/lang/Thread."<init>":(Ljava/lang/Runnable;)V
20: invokevirtual #6 // Method java/lang/Thread.start:()V
23: return
invokeinterface
public class MainKlass5 { public static void main(String[] args) { Runnable x = () -> { System.out.println("greet!"); }; x.run(); new Thread(x).start(); } }
重点是7: invokeinterface #3, 1这一行。
public static void main(java.lang.String[]);
Code:
0: invokedynamic #2, 0 // InvokeDynamic #0:run:()Ljava/lang/Runnable;
5: astore_1
6: aload_1
7: invokeinterface #3, 1 // InterfaceMethod java/lang/Runnable.run:()V
12: new #4 // class java/lang/Thread
15: dup
16: aload_1
17: invokespecial #5 // Method java/lang/Thread."<init>":(Ljava/lang/Runnable;)V
20: invokevirtual #6 // Method java/lang/Thread.start:()V
23: return
invokespecial
特殊调用。special理解为“特殊”其实并不准确,应该理解为“特定”,表示特定方法的调用,它其实是相对于“多态”的。
多态是运行时根据引用的对象实例,动态调用特定对象实例的对应方法。这种情况是invokevirtual。
而这里的“特定”并不需要运行时动态决定调用特定对象实例的对应方法,它是在编译时就已经决定了调用了哪一个方法。
隐式构造:
public class Klass0 { }
public asm.invokespecial.Klass0();
Code:
0: aload_0
1: invokespecial #1 // Method java/lang/Object."<init>":()V
4: return
显式构造
public class Klass1 { public Klass1() { } public Klass1(int i) { int i2 = i; } }
public asm.invokespecial.Klass1();
Code:
0: aload_0
1: invokespecial #1 // Method java/lang/Object."<init>":()V
4: return
public asm.invokespecial.Klass1(int);
Code:
0: aload_0
1: invokespecial #1 // Method java/lang/Object."<init>":()V
4: iload_1
5: istore_2
6: return
显式调用父类构造
public class SuperKlass1 extends Klass1 { public SuperKlass1(int i) { super(i); } }
public asm.invokespecial.SuperKlass1(int);
Code:
0: aload_0
1: iload_1
2: invokespecial #2 // Method asm/invokespecial/Klass1."<init>":(I)V
5: return
调用父类方法:
public class SuperKlass2 extends Klass2 { public void test0() { super.test0(); } }
public void test0();
Code:
0: aload_0
1: invokespecial #2 // Method asm/invokespecial/Klass2.test0:()V
4: return
构造(创建对象实例)
public class MainKlass1 { public static void main(String[] args) { SuperKlass1 klass0 = new Klass1(1); Klass1 klass1 = new Klass1(1); } }
public static void main(java.lang.String[]);
Code:
0: new #2 // class asm/invokespecial/Klass1
3: dup
4: iconst_1
5: invokespecial #3 // Method asm/invokespecial/Klass1."<init>":(I)V
8: astore_1
9: new #2 // class asm/invokespecial/Klass1
12: dup
13: iconst_1
14: invokespecial #3 // Method asm/invokespecial/Klass1."<init>":(I)V
17: astore_2
18: return
私有方法调用:
public class Klass3 { private void test0() { } public void test1() { test0(); } }
public void test1();
Code:
0: aload_0
1: invokespecial #2 // Method test0:()V
4: return
invokestatic
invokevirtual
public class MainKlass4 { public static void main(String[] args) { SuperKlass4 klass4 = new Klass4_1(); klass4.test(); klass4 = new Klass4_2(); klass4.test(); } }
public static void main(java.lang.String[]);
Code:
0: new #2 // class asm/invokespecial/Klass4_1
3: dup
4: invokespecial #3 // Method asm/invokespecial/Klass4_1."<init>":()V
7: astore_1
8: aload_1
9: invokevirtual #4 // Method asm/invokespecial/SuperKlass4.test:()V
12: new #5 // class asm/invokespecial/Klass4_2
15: dup
16: invokespecial #6 // Method asm/invokespecial/Klass4_2."<init>":()V
19: astore_1
20: aload_1
21: invokevirtual #4 // Method asm/invokespecial/SuperKlass4.test:()V
24: return
ldc指令
将运行时常量池中的item压入到操作栈。
String str = "string abc";0: ldc #2 // String string abc
2: astore_1
String str = new String("string abc");
0: new #2 // class java/lang/String
3: dup
4: ldc #3 // String string abc
6: invokespecial #4 // Method java/lang/String."<init>":(Ljava/lang/String;)V
9: astore_1
在这个例子中,将字符串"string abc"压入到操作栈。
iconst_<i>指令
将int常量i压入操作栈。如iconst_m1 = 2 (0x2), iconst_0 = 3 (0x3), iconst_1 = 4 (0x4), iconst_2 = 5 (0x5), iconst_3 = 6 (0x6), iconst_4 = 7 (0x7), iconst_5 = 8 (0x8)分别表示将-1, 0, 1, 2, 3, 4, 5压入操作栈。
5: iconst_1
类加载时,在验证阶段针对指令的类型检查,参考https://docs.oracle.com/javase/specs/jvms/se8/html/jvms-4.html#jvms-4.10.1.9,其中列举了所有在验证阶段会进行针对指令的类型检查。aaload、aastore、aconst_null、aload, aload_<n>、anewarray、areturn、arraylength、astore, astore_<n>、athrow等.
其他指令码
下面的这些指令并不在java中定义:
LoadLoad
StoreStore
LoadStore
StoreLoad
EnterLoad
EnterStore
ExitLoad
ExitStore
LoadEnter
LoadExit
StoreEnter
StoreExit
EnterEnter
ExitExit
EnterExit
ExitEnter
synchronized
public class SynchronizedTest { private int value; public synchronized void set(int value) { this.value = value; } public void set0(int value) { synchronized (this) { this.value = value; } } }
public class instruction.SynchronizedTest {
public instruction.SynchronizedTest();
Code:
0: aload_0
1: invokespecial #1 // Method java/lang/Object."<init>":()V
4: return
public synchronized void set(int);
Code:
0: aload_0
1: iload_1
2: putfield #2 // Field value:I
5: return
public void set0(int);
Code:
0: aload_0
1: dup
2: astore_2
3: monitorenter
4: aload_0
5: iload_1
6: putfield #2 // Field value:I
9: aload_2
10: monitorexit
11: goto 19
14: astore_3
15: aload_2
16: monitorexit
17: aload_3
18: athrow
19: return
Exception table:
from to target type
4 11 14 any
14 17 14 any
}
从反编译的字节码结果看,synchronized以修饰符作用在方法上,在字节码上是不体现的。但在方法内部作为同步原语使用的话,在字节码上体现在monitorenter/monitorexit上。
赋值
private int value; public void set() { value = 100; }0: aload_0
1: bipush 100
3: putfield #2 // Field value:I
6: return
volatile变量的赋值:
private volatile int j; public void test_assign() { j = 11; }
0: aload_0
1: bipush 11
3: putfield #2 // Field j:I
6: return
volatile的作用并没有体现在反编译的字节码上。这个需要在反编译到汇编才能看得到效果:
0x00c69fbd: mov $0xb,%esi
0x00c69fc2: mov %esi,0x8(%eax)
0x00c69fc5: lock addl $0x0,(%esp) ;*putfield j
; - ins.VolatileTest::test_assign@3 (line 11)
; - ins.VolatileTest::main@9 (line 16)
lock addl $0x0,(%esp)实际上就是个内存屏障,它能保证变量的可见性。至于volatile的禁止重排序作用,这里还看不到效果。
关于lock指令,x86下是这么描述的:
LOCK — Assert LOCK# Signal Prefix 写道
Description
Causes the processor’s LOCK# signal to be asserted during execution of the accompanying instruction (turns the instruction into an atomic instruction). In a multiprocessor environment, the LOCK# signal ensures that the processor has exclusive use of any shared memory while the signal is asserted.
In most IA-32 and all Intel 64 processors, locking may occur without the LOCK# signal being asserted. See the “IA-32 Architecture Compatibility” section below for more details.
The LOCK prefix can be prepended only to the following instructions and only to those forms of the instructions where the destination operand is a memory operand: ADD, ADC, AND, BTC, BTR, BTS, CMPXCHG, CMPXCH8B, CMPXCHG16B, DEC, INC, NEG, NOT, OR, SBB, SUB, XOR, XADD, and XCHG. If the LOCK prefix is used with one of these instructions and the source operand is a memory operand, an undefined opcode exception (#UD) may be generated. An undefined opcode exception will also be generated if the LOCK prefix is used with any instruction not in the above list. The XCHG instruction always asserts the LOCK# signal regardless of the presence or absence of the LOCK prefix.
The LOCK prefix is typically used with the BTS instruction to perform a read-modify-write operation on a memory location in shared memory environment.
The integrity of the LOCK prefix is not affected by the alignment of the memory field. Memory locking is observed for arbitrarily misaligned fields.
This instruction’s operation is the same in non-64-bit modes and 64-bit mode.
Causes the processor’s LOCK# signal to be asserted during execution of the accompanying instruction (turns the instruction into an atomic instruction). In a multiprocessor environment, the LOCK# signal ensures that the processor has exclusive use of any shared memory while the signal is asserted.
In most IA-32 and all Intel 64 processors, locking may occur without the LOCK# signal being asserted. See the “IA-32 Architecture Compatibility” section below for more details.
The LOCK prefix can be prepended only to the following instructions and only to those forms of the instructions where the destination operand is a memory operand: ADD, ADC, AND, BTC, BTR, BTS, CMPXCHG, CMPXCH8B, CMPXCHG16B, DEC, INC, NEG, NOT, OR, SBB, SUB, XOR, XADD, and XCHG. If the LOCK prefix is used with one of these instructions and the source operand is a memory operand, an undefined opcode exception (#UD) may be generated. An undefined opcode exception will also be generated if the LOCK prefix is used with any instruction not in the above list. The XCHG instruction always asserts the LOCK# signal regardless of the presence or absence of the LOCK prefix.
The LOCK prefix is typically used with the BTS instruction to perform a read-modify-write operation on a memory location in shared memory environment.
The integrity of the LOCK prefix is not affected by the alignment of the memory field. Memory locking is observed for arbitrarily misaligned fields.
This instruction’s operation is the same in non-64-bit modes and 64-bit mode.
而如果没有volatile修饰,反编译的汇编效果类似:
0x00c69fbd: movl $0xa,0x8(%eax) ;*putfield i
; - ins.NonvolatileTest::test_assign@3 (line 15)
; - ins.NonvolatileTest::main@9 (line 22)
如果是本地变量的赋值:
public void assign() { int i = 100; }0: bipush 100
2: istore_1
3: return
i++操作:
public class VariableSetTest { private int value; public static void main(String[] args) { VariableSetTest test = new VariableSetTest(); test.value++; System.out.println(test.value); } }public class VariableSetTest {
public VariableSetTest();
Code:
0: aload_0
1: invokespecial #1 // Method java/lang/Object."<init>":()V
4: return
public static void main(java.lang.String[]);
Code:
0: new #2 // class VariableSetTest
3: dup
4: invokespecial #3 // Method "<init>":()V
7: astore_1
8: aload_1
9: dup
10: getfield #4 // Field value:I
13: iconst_1
14: iadd
15: putfield #4 // Field value:I
18: getstatic #5 // Field java/lang/System.out:Ljava/io/PrintStream;
21: aload_1
22: getfield #4 // Field value:I
25: invokevirtual #6 // Method java/io/PrintStream.println:(I)V
28: return
}
局部变量的i++操作:
public class VariableSetTest2 { public static void main(String[] args) { int value = 0; value++; System.out.println(value); } }public class VariableSetTest2 {
public VariableSetTest2();
Code:
0: aload_0
1: invokespecial #1 // Method java/lang/Object."<init>":()V
4: return
public static void main(java.lang.String[]);
Code:
0: iconst_0
1: istore_1
2: iinc 1, 1
5: getstatic #2 // Field java/lang/System.out:Ljava/io/PrintStream;
8: iload_1
9: invokevirtual #3 // Method java/io/PrintStream.println:(I)V
12: return
}
1、VariableSetTest中的++操作对应的指令为14: iadd,但VariableSetTest2中的++操作对应的指令为2: iinc 1, 1。
2、VariableSetTest,VariableSetTest2中的++操作会更新value的值,但在反编译后的指令码中没有astore类似的指令更新本地变量表中对应变量value的值。
相关推荐
"JVM指令手册详细完整版.pdf" 本资源是关于JVM指令的详细手册,涵盖了JVM指令的各种系列命令,包括未归类系列、const系列、push系列、ldc系列、load系列等。每个系列命令都有其特定的功能和用途,下面我们将逐一...
`JVM指令手册`是深入理解JVM内部工作原理的重要参考资料,它详细列出了JVM所支持的所有操作指令,这对于Java开发者提升技能、进行性能优化以及排查问题具有极高的价值。以下是一些关键的JVM指令及相关的知识点: 1....
JVM指令集是JVM的核心组成部分,它定义了JVM能够理解和执行的一系列低级操作指令。这些指令构成了Java程序在运行时的微观世界,对理解JVM的工作原理至关重要。下面我们将深入探讨JVM指令集及其重要性。 1. **JVM...
首先,我们来了解下JVM指令的基本结构。每个字节码由一个单字节的操作码(opcode)和可能跟随的操作数组成。操作码决定了这条指令要执行的动作,比如加载、存储、运算、控制流程等。操作数则提供了指令执行所需的...
JVM指令主要分为:本地变量表到操作数栈类指令、操作数栈到本地变量表类指令、常数到操作数栈类指令、将数组指定索引的数组推送至操作数栈类指令、将操作数栈数存储到数组指定索引类指令、操作数栈其他相关类指令、...
JVM指令集是JVM内部使用的微指令集合,这些指令构成了Java字节码的基础。在《JVM指令手册》中,主要涵盖了栈和局部变量的操作,这是理解JVM工作原理的关键部分。 1. 栈和局部变量操作: JVM使用栈来存储临时计算...
JVM指令集是JVM内部的工作语言,由一系列单字节的指令组成,每条指令都有特定的功能。这份“JVM指令查询手册”很可能包含了JVM的所有公共指令、它们的含义以及使用方式。下面,我们将深入探讨JVM指令集及其在Java...
标题《00-JVM指令手册》和描述“JVM指令手册.java代码经过javap-v可以查看java指令,配合指令手册可以查看详细操作过程”表明文档是一份关于Java虚拟机(JVM)指令的手册。这份手册将涉及JVM字节码指令,这些指令是Java...
本篇将深入探讨JVM指令集,帮助你更好地理解Java程序的内部工作原理。 JVM指令集,也称为字节码指令集,是一系列二进制编码的指令,每个指令都对应一个特定的操作。这些指令在Java源代码被编译成.class文件时生成,...
JVM指令集是JVM的核心组成部分,用于定义和执行Java字节码。每条指令都是一个字节长度的操作码(opcode),后面跟着零个或多个操作数(operand),用于对JVM栈上的数据进行操作。 助记符是一种为指令提供的易于记忆...
### JVM指令手册解析 #### 一、概述 Java虚拟机(JVM)是Java语言的核心执行环境,它负责解释和执行Java字节码。为了更好地理解Java程序在JVM中的执行流程,了解JVM指令集至关重要。本文档将详细介绍《JVM指令手册》...
### JVM指令手册知识点 #### 概述 JVM(Java虚拟机)是执行Java字节码的虚拟机,其指令集是Java程序运行的基石。JVM指令手册是开发者理解和运用JVM指令的重要资料,其中详细介绍了各个指令的作用、参数以及应用场景...
本手册将深入解析JVM指令,特别是数值类型操作、常量推送、变量加载等操作指令。 首先,简单数值类型的指令操作主要指的是将基础的数值类型如int、long、float和double等推送到操作数栈顶。这类操作包括iconst_m1、...
以下是对JVM指令集的一些关键点的详细说明: 1. **常量压入栈的指令**:这些指令用于将基本类型的常量或null压入操作栈。例如,`iconst_1`将int类型的值1压入栈,`lconst_0`将long类型的值0压入栈,`aconst_null`则...
### JVM指令集详解 #### 常量加载指令 在JVM执行代码的过程中,经常会遇到需要将特定的常量加载到操作数栈的情况。这部分内容主要介绍了如何将各种类型的常量值加载到栈中。 - **`aconst_null`**:此指令用于将`...
JVM指令码表,JVM运行原理学习的必备工具。常量入栈指令、局部变量值转载到栈中指令、将栈顶值保存到局部变量中指令、wide指令、通用(无类型)栈操作指令、类型转换指令、整数运算、浮点运算等指令。
1. JVM指令类型:JVM指令可以分为不同类型,包括将常量推送到操作数栈的指令(const系列指令),以及用于将单个数值(如int、long、float、double)推送到操作数栈的指令(iconst系列、lconst系列、fconst系列、d...
JVM指令集是其内部工作原理的基础,每条指令都有特定的功能,用于构建和执行Java应用程序。下面我们将深入探讨JVM指令手册中的关键概念和知识点。 1. **字节码**:Java源代码编译后生成的是字节码文件(.class),...
jvm指令码参照表,可以根据此表找到javap命令解析出来的文件进行翻译。 0x01 aconst_null null值入栈。 0x02 iconst_m1 -1(int)值入栈。 0x03 iconst_0 0(int)值入栈。 0x04 iconst_1 1(int)...