管中窥探java虚拟机(二) - 亲密接触jvm的指令集

7 篇文章 0 订阅
6 篇文章 0 订阅

背景

之前被stackframemap属性折腾了一段时间,后来好好学习了一下class文件中的属性,解决了一些问题.最近又被tableswitch和lookupswitch指令弄的莫名其妙的崩溃, 专门记录一下java虚拟机的所有指令, 并做简单的分类, 便于以后检索.

分析java虚拟机指令的意义

越来越多的语言选择在java虚拟机上实现,比喻Scala,Clojure, groovy, Jython, JRuby等等. 利用jvm跨平台特性,java丰富的开源框架和开发环境, 这些语言起点更高,更容易成功. 学习java虚拟机的指令,其意义不言而喻.

指令格式

java虚拟机指令格式是操作码 + 操作数.
除了tableswitch 和 lookupswitch两条指令外, 其余的指令都是定长的.
其中操作码是定长1字节, 所以java虚拟机的指令个数上限是256条,其实真正的指令并没有这么多.
大部分指令没有操作数, 少数指令有一个操作数, 这个操作数占固定的字节数. tableswitch和lookupswitch两条指令操作数的个数是可变的.

指令分类

按操作数个数分

因为java虚拟机是基于操作数栈的,大部分指令都是从操作数栈中取操作数, 操作码后面没有操作数.

0 个操作数:

这类指令最多
0x00 (nop) ~ 0x0f (dconst_1)
0x1a (iload_0) ~ 0x35 (saload)
0x3b (istore_0) ~ 0x83 (lxor)
0x85 (i2l) ~ 0x98 (dcmpg)
0xac (ireturn) ~ 0xb1 (return)
0xbe (arraylength)
0xbf (athrow)
0xc2 (monitorenter)
0xc3 (monitorexit)

1个操作数:

0x10 (bipush) ~ 0x19 (aload)
0x36 (istore) ~ 0x3a (astore)
0xa9 (ret)
0xb2 (getstatic) ~0xbb (new)

不定长操作数

0xaa (tableswitch)
0xab (lookupswitch)
0xc4 (wide) 指令不能单独使用, 必须配合其他几个指令一起使用, wide指令并不算变长指令, 我把它放在这里是因为一些操作码前有该指令, 会扩展这些操作码后的操作数的字节个数.

按功能分

算术运算指令

加法 *add
减法 *sub
乘法 *mul
除法 *div
求余 *rem
取负 *neg
注:
星号 ( *)表示忽略前面的类型)

从局部变量表取数据加载到操作数栈

*load

从常量池中取数据加载到操作数栈

ldc
ldc_w
ldc2_w

加载常量到操作数栈

*const
sipush
bipush

交换栈顶2个操作数

swap

复制栈顶元素

0x59 (dup) ~ 0x5e (dup2_x2)

从操作数栈中取数据

*store

移位指令

左移 ishl lshl
右移 ishr lshr iushr lushr

逻辑运算指令

与 iand land
或 ior lor
异或 ixor lxor

局部变量自增指令

(0x84) iinc

类型转换指令

基本类型的数据转换 0x85 (i2l) ~ 0x93 (i2s)
引用类型检查 0xc0 (checkcast) ~ 0xc1 (instanceof)

比较大小指令

0x94 (lcmp) ~ 0x98 (dcmpg)

条件分支跳转指令

0x99 (ifeq) ~ 0xa6 (if_acmpne)
0xa7 (goto), 0xc8 (goto_w)
0xa8 (jsr), 0xc9 (jsr_w) 与 0xa9 (ret) 一同实现 finally 语句块
0xc6 (ifnull), 0xc7 (ifnonnull)

返回指令

0xac (ireturn) ~ 0xb1 (return)

分配对象

0xbb (new)
0xbc (newarray)
0xc5 (multianewarray)

操作类变量和调用类方法

0xb2 (getstatic)
0xb3 (putstatic)
0xb8 (invokestatic)

操作实例变量和调用实例方法

0xb4 (getfield)
0xb5 (putfield)
0xb6 (invokevirtual) // 调用实例方法
0xb7 (invokespecial) // 调用父类的构造方法
0xb9 (invokeinterface) // 调用接口方法
0xba (invokedynamic) // java se 7 中新增

抛异常指令

0xbf (athrow)

扩展局部变量表索引

0xc4 (wide)

保留指令

0xca (breakpoint) //用于断点调试
0xfe (impdep1)
0xff (impdep2) 这两条指令用于在特定硬件中使用的语言后门.

按操作数长度分

0个字节

同0个操作数

1个字节

0x10 (bipush)
0x12 (ldc)
0x15 (iload) ~ 0x19 (aload)
0x36 (istore) ~ 0x3a (astore)
0xa9 (ret)

2个字节

0x11 (sipush )
0x13 (ldc_w)
0x14 (ldc2_w)
0xb2 (getstatic) ~ 0xbb (new)
0xbd (anewarray)
0xc0 (checkcast)
0xc1 (instanceof)
0xc6 (ifnull)
0xc7 (ifnonnull)

3个字节

分配多维数组指令
0xc5 (multianewarray)

4个字节

0xc8 (goto_w)
0xc9 (jsr_w)

不定长字节

0xaa (tableswitch)
0xab (lookupswitch)

需要注意的指令

定长指令处理比较容易,对于
tableswitch
lookupswitch
wide
这三条变长指令处理需要注意:
tableswitch 和 lookupswitch 有 0 ~ 3 个字节填充, 保证后面的操作数是4字节内存对齐.
当wide 指令之后的操作码是iload,fload,aload,lload,dload,istore,fstore,astore,lstore,dstore 以及 ret 指令之
一时, 扩展形式与wide指令之后的操作码是 iinc 时, 扩展的形式并不一样.

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值