JVM 之 字节码指令
目录
一. 前言
二. 指令集
2.1. 支持的数据类型
2.2. 指令分类
三. 指令手册
3.1. 操作数栈
3.2. 运算与转换
3.3. 条件转移
3.4. 类与数组
3.5. 调度与返回加 finally
3.6. 指令手册汇总
3.7. 示例
一. 前言
字节码指令集的特点是数据量短小精干,便于传输,跨平台。同时也损失一定的解释执行效率。
1. 由 操作码 + 操作数组成。 JVM的指令由一个字节长度的、代表着某种特定操作含义的数字(称为操作码,Opcode)以及跟随其后的零至多个代表此操作所需参数(称为操作数,Operands)而构成。
2. 指令集的操作码是单字节的,总数不可能超过256条。为了尽可能获得短小精干的编译代码。
3. 操作数的长度不对齐,长度超过1字节的以big一endian顺序存储,即高位在前的字节序。如:两个无符号字节存储的值就是:(byte1<<8)|byte2。不对齐可省略很多填充和间隔符号。
4. 字节码指令流单字节对齐,但除Iableswitch和lookupswitch两个指令例外(4字节为界,少的补空)。
二. 指令集
2.1. 支持的数据类型
JVM指令集中,大多数的指令都包含了其所操作的数据类型信息。数据类型相关的操作码助记符中的首字母都跟操作的数据类型相关:i代表对int类型的数据操作,l代表 long ,s代表short,b代表byte,c代表char,f代表float,d代表double,a代表reference。
指令集并非支持所有类型,byte、char、short、boolean 等类型,都用操作数的运算类型(computational type)为int的指令来完成。byte和short类型的数据带符号扩展(sign-extend)为相应的int类型数据,boolean和char类型数据零位扩展(zero-extend)为相应的int类型数据。
2.2. 指令分类
1. 加载和存储指令,加载存储指令用于局部变量与操作数栈交换数据以及常量装载到操作数栈,如push、load、store、const。按数据类型不同在指令前面加i/l/f/d/a等,操作数放指令后面,超过4直接写下标,例:iload_3、iload 4。
2. 运算指令,加add、减sub、乘mul、除div、求余rem、取反neg、移位sh(l左r右)、与and、或or、异或xor、自增inc、cmp比较。
3. 类型转换指令,2(to),表示操作类型 到 目标类型,从小字节类型转大是宽化指令,从大字节类型转小窄化指令。
4. 对象创建与访问指令,创建对象new、创建基本类型数组newarray、创建引用类型数组anewarray、创建多维数组multianewarray。
5. 操作数栈管理指令,出栈pop、交换swap、复制栈顶并压栈dup。
6. 控制转移指令,条件跳转指令:if与eq/ne/lt/le/gt/ge组合(与0比较或加cmp表示栈顶两个操作数比较)、复合条件跳转指令tableswitch与lookupswitch、无条件跳转指令goto。
7. 方法调用和返回指令,调用对象的实例方法invokevirtual、调用接口方法invokeinterface、调用特殊实例方法invokespecial(如初始化方法)、调用类静态方法invokestatic、调用动态链接方法invokedynamic。返回指令return(带类型)、(以及SE6之前的jsr、ret)。
8. 异常处理指令,athrow。
9. 同步指令,monitorenter monitorexit。
三. 指令手册
3.1. 操作数栈
| 过程 | 符号 |
|---|---|
| 变量到操作数栈 | iload, iload_, lload, lload_, fload, fload_, dload, dload_, aload, aload_ |
| 操作数栈到变量 | istore, istore_, lstore, lstore_, fstore, fstore_, dstore, dstor_, astore, astore_ |
| 常数到操作数栈 | bipush, sipush, ldc, ldc_w, ldc2_w, aconst_null, iconst_ml, iconst_, lconst_, fconst_, dconst_ |
| 把数据装载到操作数栈 | baload, caload, saload, iaload, laload, faload, daload, aaload |
| 从操作数栈存存储到数组 | bastore, castore, sastore, iastore, lastore, fastore, dastore, aastore |
| 操作数栈管理 | pop, pop2, dup, dup2, dup_xl, dup2_xl, dup_x2, dup2_x2, swap |
3.2. 运算与转换
| 过程 | 符号 |
|---|---|
| 加 | iadd, ladd, fadd, dadd |
| 减 | is, ls, fs, ds |
| 乘 | imul, lmul, fmul, dmul |
| 除 | idiv, ldiv, fdiv, ddiv |
| 余数 | irem, lrem, frem, drem |
| 取负 | ineg, lneg, fneg, dneg |
| 移位 | ishl, lshr, iushr, lshl, lshr, lushr |
| 按位或 | ior, lor |
| 按位与 | iand, land |
| 按位异或 | ixor, lxor |
| 类型转换 | i2l, i2f, i2d, l2f, l2d, f2d(放宽数值转换); i2b, i2c, i2s, l2i, f2i, f2l, d2i, d2l, d2f(缩窄数值转换) |
3.3. 条件转移
| 过程 | 符号 |
|---|---|
| 有条件转移 | ifeq, iflt, ifle, ifne, ifgt, ifge, ifnull, ifnonnull, if_icmpeq, if_icmpene, if_icmplt, if_icmpgt, if_icmple, if_icmpge, if_acmpeq, if_acmpne, lcmp, fcmpl, fcmpg, dcmpl, dcmpg |
| 复合条件转移 | tableswitch, lookupswitch |
| 无条件转移 | goto, goto_w, jsr, jsr_w, ret |
3.4. 类与数组
| 过程 | 符号 |
|---|---|
| 创建类实便 | new |
| 创建新数组 | newarray, anewarray, multianwarray |
| 访问类的域和类实例域 | getfield, putfield, getstatic, putstatic |
| 获取数组长度 | arraylength |
| 检相类实例或数组属性 | instanceof, checkcast |
3.5. 调度与返回加 finally
| 过程 | 符号 |
|---|---|
| 调度对象的实便方法 | invokevirt l |
| 调用由接口实现的方法 | invokeinterface |
| 调用需要特殊处理的实例方法 | invokespecial |
| 调用命名类中的静态方法 | invokestatic |
| 方法返回 | ireturn, lreturn, freturn, dreturn, areturn, return |
| 异常 | athrow |
| finally 关键字的实现使用 | jsr, jsr_w, ret |
3.6. 指令手册汇总
| 指令码 | 助记符 | 说明 |
|---|---|---|
| 0x00 | nop | 什么都不做 |
| 0x01 | aconst_null | 将 null 推送至栈顶 |
| 0x02 | iconst_m1 | 将 int 型 -1 推送至栈顶 |
| 0x03 | iconst_0 | 将 int 型 0 推送至栈顶 |
| 0x04 | iconst_1 | 将 int 型 1 推送至栈顶 |
| 0x05 | iconst_2 | 将 int 型 2 推送至栈顶 |
| 0x06 | iconst_3 | 将 int 型 3 推送至栈顶 |
| 0x07 | iconst_4 | 将 int 型 4 推送至栈顶 |
| 0x08 | iconst_5 | 将 int 型 5 推送至栈顶 |
| 0x09 | lconst_0 | 将 long 型 0 推送至栈顶 |
| 0x0a | lconst_1 | 将 long 型 1 推送至栈顶 |
| 0x0b | fconst_0 | 将 float 型 0 推送至栈顶 |
| 0x0c | fconst_1 | 将 float 型 1 推送至栈顶 |
| 0x0d | fconst_2 | 将 float 型 2 推送至栈顶 |
| 0x0e | dconst_0 | 将 double 型 0 推送至栈顶 |
| 0x0f | dconst_1 | 将 double 型 1 推送至栈顶 |
| 0x10 | bipush | 将单字节的常量值 (-128~127) 推送至栈顶 |
| 0x11 | sipush | 将一个短整型常量值 (-32768~32767) 推送至栈顶 |
| 0x12 | ldc | 将int, |
| 0x13 | ldc_w | 将int, |
| 0x14 | ldc2_w | 将 long 或 double 型常量值从常量池中推送至栈顶(宽索引) |
| 0x15 | iload | 将指定的 int 型本地变量推送至栈顶 |
| 0x16 | lload | 将指定的 long 型本地变量推送至栈顶 |
| 0x17 | fload | 将指定的 float 型本地变量推送至栈顶 |
| 0x18 | dload | 将指定的 double 型本地变量推送至栈顶 |
| 0x19 | aload | 将指定的引用类型本地变量推送至栈顶 |
| 0x1a | iload_0 | 将第一个 int 型本地变量推送至栈顶 |
| 0x1b | iload_1 | 将第二个 int 型本地变量推送至栈顶 |
| 0x1c | iload_2 | 将第三个 int 型本地变量推送至栈顶 |
| 0x1d | iload_3 | 将第四个 int 型本地变量推送至栈顶 |
| 0x1e | lload_0 | 将第一个 long 型本地变量推送至栈顶 |
| 0x1f | lload_1 | 将第二个 long 型本地变量推送至栈顶 |
| 0x20 | lload_2 | 将第三个 long 型本地变量推送至栈顶 |
| 0x21 | lload_3 | 将第四个 long 型本地变量推送至栈顶 |
| 0x22 | fload_0 | 将第一个 float 型本地变量推送至栈顶 |
| 0x23 | fload_1 | 将第二个 float 型本地变量推送至栈顶 |
| 0x24 | fload_2 | 将第三个 float 型本地变量推送至栈顶 |
| 0x25 | fload_3 | 将第四个 float 型本地变量推送至栈顶 |
| 0x26 | dload_0 | 将第一个 double 型本地变量推送至栈顶 |
| 0x27 | dload_1 | 将第二个 double 型本地变量推送至栈顶 |
| 0x28 | dload_2 | 将第三个 double 型本地变量推送至栈顶 |
| 0x29 | dload_3 | 将第四个 double 型本地变量推送至栈顶 |
| 0x2a | aload_0 | 将第一个引用类型本地变量推送至栈顶 |
| 0x2b | aload_1 | 将第二个引用类型本地变量推送至栈顶 |
| 0x2c | aload_2 | 将第三个引用类型本地变量推送至栈顶 |
| 0x2d | aload_3 | 将第四个引用类型本地变量推送至栈顶 |
| 0x2e | iaload | 将 int 型数组指定索引的值推送至栈顶 |
| 0x2f | laload | 将 long 型数组指定索引的值推送至栈顶 |
| 0x30 | faload | 将 float 型数组指定索引的值推送至栈顶 |
| 0x31 | daload | 将 double 型数组指定索引的值推送至栈顶 |
| 0x32 | aaload | 将引用型数组指定索引的值推送至栈顶 |
| 0x33 | baload | 将 boolean 或 byte 型数组指定索引的值推送至栈顶 |
| 0x34 | caload | 将 char 型数组指定索引的值推送至栈顶 |
| 0x35 | saload | 将 short 型数组指定索引的值推送至栈顶 |
| 0x36 | istore | 将栈顶 int 型数值存入指定本地变量 |
| 0x37 | lstore | 将栈顶 long 型数值存入指定本地变量 |
| 0x38 | fstore | 将栈顶 float 型数值存入指定本地变量 |
| 0x39 | dstore | 将栈顶 double 型数值存入指定本地变量 |
| 0x3a | astore | 将栈顶引用型数值存入指定本地变量 |
| 0x3b | istore_0 | 将栈顶 int 型数值存入第一个本地变量 |
| 0x3c | istore_1 | 将栈顶 int 型数值存入第二个本地变量 |
| 0x3d | istore_2 | 将栈顶 int 型数值存入第三个本地变量 |
| 0x3e | istore_3 | 将栈顶 int 型数值存入第四个本地变量 |
| 0x3f | lstore_0 | 将栈顶 long 型数值存入第一个本地变量 |
| 0x40 | lstore_1 | 将栈顶 long 型数值存入第二个本地变量 |
| 0x41 | lstore_2 | 将栈顶 long 型数值存入第三个本地变量 |
| 0x42 | lstore_3 | 将栈顶 long 型数值存入第四个本地变量 |
| 0x43 | fstore_0 | 将栈顶 float 型数值存入第一个本地变量 |
| 0x44 | fstore_1 | 将栈顶 float 型数值存入第二个本地变量 |
| 0x45 | fstore_2 | 将栈顶 float 型数值存入第三个本地变量 |
| 0x46 | fstore_3 | 将栈顶 float 型数值存入第四个本地变量 |
| 0x47 | dstore_0 | 将栈顶 double 型数值存入第一个本地变量 |
| 0x48 | dstore_1 | 将栈顶 double 型数值存入第二个本地变量 |
| 0x49 | dstore_2 | 将栈顶 double 型数值存入第三个本地变量 |
| 0x4a | dstore_3 | 将栈顶 double 型数值存入第四个本地变量 |
| 0x4b | astore_0 | 将栈顶引用型数值存入第一个本地变量 |
| 0x4c | astore_1 | 将栈顶引用型数值存入第二个本地变量 |
| 0x4d | astore_2 | 将栈顶引用型数值存入第三个本地变量 |
| 0x4e | astore_3 | 将栈顶引用型数值存入第四个本地变量 |
| 0x4f | iastore | 将栈顶 int 型数值存入指定数组的指定索引位置 |
| 0x50 | lastore | 将栈顶 long 型数值存入指定数组的指定索引位置 |
| 0x51 | fastore | 将栈顶 float 型数值存入指定数组的指定索引位置 |
| 0x52 | dastore | 将栈顶 double 型数值存入指定数组的指定索引位置 |
| 0x53 | aastore | 将栈顶引用型数值存入指定数组的指定索引位置 |
| 0x54 | bastore | 将栈顶 boolean 或 byte 型数值存入指定数组的指定索引位置 |
| 0x55 | castore | 将栈顶 char 型数值存入指定数组的指定索引位置 |
| 0x56 | sastore | 将栈顶 short 型数值存入指定数组的指定索引位置 |
| 0x57 | pop | 将栈顶数值弹出 |
| 0x58 | pop2 | 将栈顶的一个(long 或 double 类型的)或两个数值弹出(其它) |
| 0x59 | dup | 复制栈顶数值并将复制值压入栈顶 |
| 0x5a | dup_x1 | 复制栈顶数值并将两个复制值压入栈顶 |
| 0x5b | dup_x2 | 复制栈顶数值并将三个(或两个)复制值压入栈顶 |
| 0x5c | dup2 | 复制栈顶一个(long 或 double 类型的)或两个(其它)数值并将复制值压入栈顶 |
| 0x5d | dup2_x1 | <待补充> |
| 0x5e | dup2_x2 | <待补充> |
| 0x5f | swap | 将栈最顶端的两个数值互换(数值不能是 long 或 double 类型的) |
| 0x60 | iadd | 将栈顶两 int 型数值相加并将结果压入栈顶 |
| 0x61 | ladd | 将栈顶两 long 型数值相加并将结果压入栈顶 |
| 0x62 | fadd | 将栈顶两 float 型数值相加并将结果压入栈顶 |
| 0x63 | dadd | 将栈顶两 double 型数值相加并将结果压入栈顶 |
| 0x64 | isub | 将栈顶两 int 型数值相减并将结果压入栈顶 |
| 0x65 | lsub | 将栈顶两 long 型数值相减并将结果压入栈顶 |
| 0x66 | fsub | 将栈顶两 float 型数值相减并将结果压入栈顶 |
| 0x67 | dsub | 将栈顶两 double 型数值相减并将结果压入栈顶 |
| 0x68 | imul | 将栈顶两 int 型数值相乘并将结果压入栈顶 |
| 0x69 | lmul | 将栈顶两 long 型数值相乘并将结果压入栈顶 |
| 0x6a | fmul | 将栈顶两 float 型数值相乘并将结果压入栈顶 |
| 0x6b | dmul | 将栈顶两 double 型数值相乘并将结果压入栈顶 |
| 0x6c | idiv | 将栈顶两 int 型数值相除并将结果压入栈顶 |
| 0x6d | ldiv | 将栈顶两 long 型数值相除并将结果压入栈顶 |
| 0x6e | fdiv | 将栈顶两 float 型数值相除并将结果压入栈顶 |
| 0x6f | ddiv | 将栈顶两 double 型数值相除并将结果压入栈顶 |
| 0x70 | irem | 将栈顶两 int 型数值作取模运算并将结果压入栈顶 |
| 0x71 | lrem | 将栈顶两 long 型数值作取模运算并将结果压入栈顶 |
| 0x72 | frem | 将栈顶两 float 型数值作取模运算并将结果压入栈顶 |
| 0x73 | drem | 将栈顶两 double 型数值作取模运算并将结果压入栈顶 |
| 0x74 | ineg | 将栈顶 int 型数值取负并将结果压入栈顶 |
| 0x75 | lneg | 将栈顶 long 型数值取负并将结果压入栈顶 |
| 0x76 | fneg | 将栈顶 float 型数值取负并将结果压入栈顶 |
| 0x77 | dneg | 将栈顶 double 型数值取负并将结果压入栈顶 |
| 0x78 | ishl | 将 int 型数值左移位指定位数并将结果压入栈顶 |
| 0x79 | lshl | 将 long 型数值左移位指定位数并将结果压入栈顶 |
| 0x7a | ishr | 将 int 型数值右(符号)移位指定位数并将结果压入栈顶 |
| 0x7b | lshr | 将 long 型数值右(符号)移位指定位数并将结果压入栈顶 |
| 0x7c | iushr | 将 int 型数值右(无符号)移位指定位数并将结果压入栈顶 |
| 0x7d | lushr | 将 long 型数值右(无符号)移位指定位数并将结果压入栈顶 |
| 0x7e | iand | 将栈顶两 int 型数值作“按位与”并将结果压入栈顶 |
| 0x7f | land | 将栈顶两 long 型数值作“按位与”并将结果压入栈顶 |
| 0x80 | ior | 将栈顶两 int 型数值作“按位或”并将结果压入栈顶 |
| 0x81 | lor | 将栈顶两 long 型数值作“按位或”并将结果压入栈顶 |
| 0x82 | ixor | 将栈顶两 int 型数值作“按位异或”并将结果压入栈顶 |
| 0x83 | lxor | 将栈顶两 long 型数值作“按位异或”并将结果压入栈顶 |
| 0x84 | iinc | 将指定 int 型变量增加指定值(i++, |
| 0x85 | i2l | 将栈顶 int 型数值强制转换成 long 型数值并将结果压入栈顶 |
| 0x86 | i2f | 将栈顶 int 型数值强制转换成 float 型数值并将结果压入栈顶 |
| 0x87 | i2d | 将栈顶 int 型数值强制转换成 double 型数值并将结果压入栈顶 |
| 0x88 | l2i | 将栈顶 long 型数值强制转换成 int 型数值并将结果压入栈顶 |
| 0x89 | l2f | 将栈顶 long 型数值强制转换成 float 型数值并将结果压入栈顶 |
| 0x8a | l2d | 将栈顶 long 型数值强制转换成 double 型数值并将结果压入栈顶 |
| 0x8b | f2i | 将栈顶 float 型数值强制转换成 int 型数值并将结果压入栈顶 |
| 0x8c | f2l | 将栈顶 float 型数值强制转换成 long 型数值并将结果压入栈顶 |
| 0x8d | f2d | 将栈顶 float 型数值强制转换成 double 型数值并将结果压入栈顶 |
| 0x8e | d2i | 将栈顶 double 型数值强制转换成 int 型数值并将结果压入栈顶 |
| 0x8f | d2l | 将栈顶 double 型数值强制转换成 long 型数值并将结果压入栈顶 |
| 0x90 | d2f | 将栈顶 double 型数值强制转换成 float 型数值并将结果压入栈顶 |
| 0x91 | i2b | 将栈顶 int 型数值强制转换成 byte 型数值并将结果压入栈顶 |
| 0x92 | i2c | 将栈顶 int 型数值强制转换成 char 型数值并将结果压入栈顶 |
| 0x93 | i2s | 将栈顶 int 型数值强制转换成 short 型数值并将结果压入栈顶 |
| 0x94 | lcmp | 比较栈顶两 long 型数值大小,并将结果(1,0,-1)压入栈顶 |
| 0x95 | fcmpl | 比较栈顶两 float 型数值大小,并将结果(1,0,-1)压入栈顶;当其中一个数值为 NaN 时,将 -1 压入栈顶 |
| 0x96 | fcmpg | 比较栈顶两 float 型数值大小,并将结果(1,0,-1)压入栈顶;当其中一个数值为 NaN 时,将 1 压入栈顶 |
| 0x97 | dcmpl | 比较栈顶两 double 型数值大小,并将结果(1,0,-1)压入栈顶;当其中一个数值为 NaN 时,将 -1 压入栈顶 |
| 0x98 | dcmpg | 比较栈顶两 double 型数值大小,并将结果(1,0,-1)压入栈顶;当其中一个数值为 NaN 时,将 1 压入栈顶 |
| 0x99 | ifeq | 当栈顶 int 型数值等于 0 时跳转 |
| 0x9a | ifne | 当栈顶 int 型数值不等于 0 时跳转 |
| 0x9b | iflt | 当栈顶 int 型数值小于 0 时跳转 |
| 0x9c | ifge | 当栈顶 int 型数值大于等于 0 时跳转 |
| 0x9d | ifgt | 当栈顶 int 型数值大于 0 时跳转 |
| 0x9e | ifle | 当栈顶 int 型数值小于等于 0 时跳转 |
| 0x9f | if_icmpeq | 比较栈顶两 int 型数值大小,当结果等于 0 时跳转 |
| 0xa0 | if_icmpne | 比较栈顶两 int 型数值大小,当结果不等于 0 时跳转 |
| 0xa1 | if_icmplt | 比较栈顶两 int 型数值大小,当结果小于 0 时跳转 |
| 0xa2 | if_icmpge | 比较栈顶两 int 型数值大小,当结果大于等于 0 时跳转 |
| 0xa3 | if_icmpgt | 比较栈顶两 int 型数值大小,当结果大于 0 时跳转 |
| 0xa4 | if_icmple | 比较栈顶两 int 型数值大小,当结果小于等于 0 时跳转 |
| 0xa5 | if_acmpeq | 比较栈顶两引用型数值,当结果相等时跳转 |
| 0xa6 | if_acmpne | 比较栈顶两引用型数值,当结果不相等时跳转 |
| 0xa7 | goto | 无条件跳转 |
| 0xa8 | jsr | 跳转至指定 16 位 offset 位置,并将 jsr 下一条指令地址压入栈顶 |
| 0xa9 | ret | 返回至本地变量指定的 index 的指令位置(一般与 jsr, jsr_w 联合使用) |
| 0xaa | tableswitch | 用于 switch 条件跳转,case 值连续(可变长度指令) |
| 0xab | lookupswitch | 用于 switch 条件跳转,case 值不连续(可变长度指令) |
| 0xac | ireturn | 从当前方法返回 int |
| 0xad | lreturn | 从当前方法返回 long |
| 0xae | freturn | 从当前方法返回 float |
| 0xaf | dreturn | 从当前方法返回 double |
| 0xb0 | areturn | 从当前方法返回对象引用 |
| 0xb1 | return | 从当前方法返回void |
| 0xb2 | getstatic | 获取指定类的静态域,并将其值压入栈顶 |
| 0xb3 | putstatic | 为指定的类的静态域赋值 |
| 0xb4 | getfield | 获取指定类的实例域,并将其值压入栈顶 |
| 0xb5 | putfield | 为指定的类的实例域赋值 |
| 0xb6 | invokevirtual | 调用实例方法 |
| 0xb7 | invokespecial | 调用超类构造方法,实例初始化方法,私有方法 |
| 0xb8 | invokestatic | 调用静态方法 |
| 0xb9 | invokeinterface | 调用接口方法 |
| 0xba | – | |
| 0xbb | new | 创建一个对象,并将其引用值压入栈顶 |
| 0xbc | newarray | 创建一个指定原始类型(如int, float, char…)的数组,并将其引用值压入栈顶 |
| 0xbd | anewarray | 创建一个引用型(如类,接口,数组)的数组,并将其引用值压入栈顶 |
| 0xbe | arraylength | 获得数组的长度值并压入栈顶 |
| 0xbf | athrow | 将栈顶的异常抛出 |
| 0xc0 | checkcast | 检验类型转换,检验未通过将抛出 ClassCastException |
| 0xc1 | instanceof | 检验对象是否是指定的类的实例,如果是将 1 压入栈顶,否则将0压入栈顶 |
| 0xc2 | monitorenter | 获得对象的锁,用于同步方法或同步块 |
| 0xc3 | monitorexit | 释放对象的锁,用于同步方法或同步块 |
| 0xc4 | wide | <待补充> |
| 0xc5 | multianewarray | 创建指定类型和指定维度的多维数组(执行该指令时,操作栈中必须包含各维度的长度值),并将其引用值压入栈顶 |
| 0xc6 | ifnull | 为 null 时跳转 |
| 0xc7 | ifnonnull | 不为 null 时跳转 |
| 0xc8 | goto_w | 无条件跳转(宽索引) |
| 0xc9 | jsr_w | 跳转至指定 32 位 offset 位置,并将 jsr_w 下一条指令地址压入栈顶 |
3.7. 示例
public void onlyMe(Foo f) {synchronized(f) {doSomething();}
}
编译后,这段代码生成的字节码序列如下:
Method void onlyMe(Foo)
0 aload_1 // 将对象 f 入栈
1 dup // 复制栈顶元素(即 f 的引用)
2 astore_2 // 将栈顶元素存储到局部变量表 Slot 2 中
3 monitorenter // 以栈顶元素(即 f)作为锁,开始同步
4 aload_0 // 将局部变量 Slot 0(即 this 指针)的元素入栈
5 invokevirtual #5 // 调用 doSomething() 方法
8 aload_2 // 将局部变量 Slot 2 的元素(即 f)入栈
9 monitorexit // 退出同步
10 goto 18 // 方法正常结束,跳转到 18 返回
13 astore_3 // 从这步开始是异常路径,见下面异常表的 Target 13
14 aload_2 // 将局部变量 Slot 2 的元素(即 f)入栈
15 monitorexit // 退出同步
16 aload_3 // 将局部变量 Slot 3 的元素(即异常对象)入栈
17 athrow // 把异常对象重新抛出给 onlyMe() 方法的调用者
18 return // 方法正常返回Exception table:
FromTo Target Type4 10 13 any13 16 13 any
相关文章:
JVM 之 字节码指令
目录 一. 前言 二. 指令集 2.1. 支持的数据类型 2.2. 指令分类 三. 指令手册 3.1. 操作数栈 3.2. 运算与转换 3.3. 条件转移 3.4. 类与数组 3.5. 调度与返回加 finally 3.6. 指令手册汇总 3.7. 示例 一. 前言 字节码指令集的特点是数据量短小精干,便于传…...
阿里云跨账号建立局域网
最近有活动,和好友一并薅了下阿里云的羊毛。琢磨着两台机器组一个局域网,于是有了这个需求,把步骤记录一下: 假设两台机器叫A和B,我们开始进行建立和组网 1. 建立ECS 把A机器公共环境装好,然后使用《实例与…...
【OpenSTL】方便好用的时空预测开源库
OpenSTL:方便好用的时空预测开源库 时空预测学习是一种学习范式,它使得模型能够通过在无监督的情况下从给定的过去帧预测未来帧,从而学习空间和时间的模式。尽管近年来取得了显著的进展,但由于不同的设置、复杂的实现和难以复现性…...
【Unity】IBeginDragHandler、IDragHandler 和 IEndDragHandler 介绍
IBeginDragHandler、IDragHandler 和 IEndDragHandler 介绍 IBeginDragHandler、IDragHandler 和 IEndDragHandler 是 Unity 引擎中的三个接口,用于处理 UI 元素的拖放事件。这些接口通常结合使用,构成了 Unity 引擎的拖放事件系统。 IBeginDragHandler…...
杰发科技AC7801——Flash模拟EEP内存分布情况
简介 本文记录了在使用AutoChips芯片Flash模拟EEP过程中的一些理解 核心代码如下 #include <stdlib.h> #include "ac780x_sweeprom.h" #include "ac780x_debugout.h"#define SWEEPROM_SIZE (2048UL) /* Ssoftware eeprom size(Byte) */ #define TE…...
【前端知识】Node——http模块url模块的常用操作
一、创建简易Server const http require(http); const URL require(url);const HTTP_PORT 8088;const server http.createServer((req, res) > {// req:request请求对象,包含请求相关的信息;// res:response响应对象&…...
平衡二叉树 (简单易懂)
目录 一、概念 二、性质 三、插入操作 四、旋转操作 五、删除操作 六、代码实现 七、复杂度 一、概念 平衡二叉树(Balanced Binary Tree)是一种特殊的二叉搜索树(Binary Search Tree,BST),它在插入和…...
Vue.observable 是什么
Observable 翻译过来我们可以理解成可观察的 Vue.js2.6 新增 Vue.observable,让一个对象变成响应式数据。Vue 内部会用它来处理 data 函数返回的对象 。 返回的对象可以直接用于渲染函数和计算属性内,并且会在发生变更时触发相应的更新。也可以作为最小化…...
【五年创作纪念日】
机缘 我成为创作者的过程并不复杂,可以说是一个自然的发展。我是一名软件工程师,日常的工作主要是编程和解决问题。在工作的过程中,我发现有很多时候我需要查找一些特定的技术问题或者寻找一些最佳实践来解决我遇到的问题。在这个过程中&…...
数据库基础入门 — SQL排序与分页
我是南城余!阿里云开发者平台专家博士证书获得者! 欢迎关注我的博客!一同成长! 一名从事运维开发的worker,记录分享学习。 专注于AI,运维开发,windows Linux 系统领域的分享! 本…...
WordPress站点屏蔽过滤垃圾评论教程(Akismet反垃圾评论插件)
前段时间我的WordPress站点经常收到垃圾评论的轰炸,严重时一天会收到几十条垃圾评论。我这个小破站一没啥流量,二又不盈利,实在是不太理解为啥有人要这么执着地浪费资源在上面。 Akismet反垃圾评论插件 其实用了 Akismet 反垃圾评论插件后&a…...
Modbus-RTU协议讲解与实战
1、背景 工作需要,需要使用Modbus-RTU实现RS485通信,于是简单学习并实践了一下。 2、参考资料 一文看懂Modbus协议 3、协议说明 3.1、协议类型 当前设备采用Modbus-RTU协议,采用CRC-16_Modbus校验算法,数据链路层使用用标准串口协议,物理层采用RS485进行数据传输。 …...
数据结构 查找基本概念
敬请期待。。。 1. 适用于折半查找的表的存储方式及元素排列要求为(顺序方式存储,元素有序 )。 2. 有一个按元素值排好序的顺序表(长度大于2),分别用顺序查找和折半查找与给定值相等的元素,比较次数分别是s和b&am…...
『Linux升级路』基础开发工具——gcc/g++篇
🔥博客主页:小王又困了 📚系列专栏:Linux 🌟人之为学,不日近则日退 ❤️感谢大家点赞👍收藏⭐评论✍️ 目录 一、快速认识gcc/g 二、预处理 📒1.1头文件展开 📒1…...
面试:RocketMQ相关问题
文章目录 什么是 RocketMQ,有哪些使用场景?RocketMQ 由哪些⻆色组成,每个⻆色作用和特点是什么?RocketMQ 中的 Topic 和 JMS 的 queue 有什么区别?RocketMQ 消费模式有几种?RocketMQ 的 Consumer 是如何消费…...
2304. 网格中的最小路径代价 : 从「图论最短路」过渡到「O(1) 空间的原地模拟」
题目描述 这是 LeetCode 上的 「2304. 网格中的最小路径代价」 ,难度为 「中等」。 Tag : 「最短路」、「图」、「模拟」、「序列 DP」、「动态规划」 给你一个下标从 0 开始的整数矩阵 grid,矩阵大小为 m x n,由从 0 到 的不同整数组成。 你…...
【机器学习】算法性能评估常用指标总结
考虑一个二分问题,即将实例分成正类(positive)或负类(negative)。对一个二分问题来说,会出现四种情况。如果一个实例是正类并且也被 预测成正类,即为真正类(True positive࿰…...
前端 JavaScript 与 HTML 怎么实现交互?
前端的交互性是通过JavaScript与HTML结合实现的。JavaScript作为一种脚本语言,可以嵌入HTML中,通过对DOM(文档对象模型)的操作,实现与用户的交互。以下将详细介绍前端JavaScript与HTML如何实现交互,包括事件…...
命令执行总结
之前做了一大堆的题目 都没有进行总结 现在来总结一下命令执行 我遇到的内容 这里我打算按照过滤进行总结 依据我做过的题目 过滤system 下面是一些常见的命令执行内容 system() passthru() exec() shell_exec() popen() proc_open() pcntl_exec() 反引号 同shell_exec() …...
机器学习——词向量模型(CBOW代码实现-未开始)
本来是不打算做这个CBOW代码案例的,想快马加鞭看看前馈神经网络 毕竟书都买好了 可是…可是…我看书的时候,感觉有点儿困难,哭的很大声… 感觉自己脑细胞可能无法这么快接受 要不,还是退而求个稍微难度没那么大的事,想…...
华为云AI开发平台ModelArts
华为云ModelArts:重塑AI开发流程的“智能引擎”与“创新加速器”! 在人工智能浪潮席卷全球的2025年,企业拥抱AI的意愿空前高涨,但技术门槛高、流程复杂、资源投入巨大的现实,却让许多创新构想止步于实验室。数据科学家…...
《Playwright:微软的自动化测试工具详解》
Playwright 简介:声明内容来自网络,将内容拼接整理出来的文档 Playwright 是微软开发的自动化测试工具,支持 Chrome、Firefox、Safari 等主流浏览器,提供多语言 API(Python、JavaScript、Java、.NET)。它的特点包括&a…...
大数据零基础学习day1之环境准备和大数据初步理解
学习大数据会使用到多台Linux服务器。 一、环境准备 1、VMware 基于VMware构建Linux虚拟机 是大数据从业者或者IT从业者的必备技能之一也是成本低廉的方案 所以VMware虚拟机方案是必须要学习的。 (1)设置网关 打开VMware虚拟机,点击编辑…...
全志A40i android7.1 调试信息打印串口由uart0改为uart3
一,概述 1. 目的 将调试信息打印串口由uart0改为uart3。 2. 版本信息 Uboot版本:2014.07; Kernel版本:Linux-3.10; 二,Uboot 1. sys_config.fex改动 使能uart3(TX:PH00 RX:PH01),并让boo…...
JVM虚拟机:内存结构、垃圾回收、性能优化
1、JVM虚拟机的简介 Java 虚拟机(Java Virtual Machine 简称:JVM)是运行所有 Java 程序的抽象计算机,是 Java 语言的运行环境,实现了 Java 程序的跨平台特性。JVM 屏蔽了与具体操作系统平台相关的信息,使得 Java 程序只需生成在 JVM 上运行的目标代码(字节码),就可以…...
AirSim/Cosys-AirSim 游戏开发(四)外部固定位置监控相机
这个博客介绍了如何通过 settings.json 文件添加一个无人机外的 固定位置监控相机,因为在使用过程中发现 Airsim 对外部监控相机的描述模糊,而 Cosys-Airsim 在官方文档中没有提供外部监控相机设置,最后在源码示例中找到了,所以感…...
现有的 Redis 分布式锁库(如 Redisson)提供了哪些便利?
现有的 Redis 分布式锁库(如 Redisson)相比于开发者自己基于 Redis 命令(如 SETNX, EXPIRE, DEL)手动实现分布式锁,提供了巨大的便利性和健壮性。主要体现在以下几个方面: 原子性保证 (Atomicity)ÿ…...
如何应对敏捷转型中的团队阻力
应对敏捷转型中的团队阻力需要明确沟通敏捷转型目的、提升团队参与感、提供充分的培训与支持、逐步推进敏捷实践、建立清晰的奖励和反馈机制。其中,明确沟通敏捷转型目的尤为关键,团队成员只有清晰理解转型背后的原因和利益,才能降低对变化的…...
Vue 模板语句的数据来源
🧩 Vue 模板语句的数据来源:全方位解析 Vue 模板(<template> 部分)中的表达式、指令绑定(如 v-bind, v-on)和插值({{ }})都在一个特定的作用域内求值。这个作用域由当前 组件…...
保姆级【快数学会Android端“动画“】+ 实现补间动画和逐帧动画!!!
目录 补间动画 1.创建资源文件夹 2.设置文件夹类型 3.创建.xml文件 4.样式设计 5.动画设置 6.动画的实现 内容拓展 7.在原基础上继续添加.xml文件 8.xml代码编写 (1)rotate_anim (2)scale_anim (3)translate_anim 9.MainActivity.java代码汇总 10.效果展示 逐帧…...
