Java虚拟级的指令由一个字节长度,代表着某种特定操作含义(成为操作码Opcode)以及跟随其后的零个至多个代表该操作所需要的参数(称为操作数参数Oprands)而构成由于Java虚拟机采用面向操作数栈编程而不是寄存器架构,所以大多数指令都不包含操作数,只有一个操作码。

 

一.加载和存储指令

 

1.将一个局部变量表中的数据加载到操作数栈:(i,l,f,d,a)load(int,long,float,double,a表示引用)。

2.将一个数值从操作数栈存储到局部变量表:(i,l,f,d,a)store。

3.将一个常量加载到操作数栈:bipush(将单字节常量(-128~127)值加载到操作数栈),sipush(将一个短整型常量值(-32768~32767)加载到操作数栈),ldc(将int,float,String型的常量加载到操作数栈),iconst(将int类型的数据加载到操作数栈)等。。。

4.扩充局部变量表的访问索引指令:wide。

 

大家可能产生疑问:你说了那么操作数栈操作数栈到底什么是操作数栈呢?操作数栈就是你个用栈这个数据结构来存储我们的操作数,每次把操作数推入栈中,当需要计算的时候再把操作数从操作数栈弹出来运算符完成后再存到栈帧中的局部变量表中。

 

字节码指令介绍——深入理解Java虚拟机

字节码指令介绍——深入理解Java虚拟机

 

 通过对方法表集合中main方法的Code属性的观察我们就可以看很多我们上面说到的指令。

public class Hello{
	
	public static void main(String[] args){
		
		int a=10;
		int b=20;
		int c=a+b;


		float d=10;
		float e=20;
		float f=d+e;
	}
}

上面这一段Java源代码的main方法的Code属性对应的指令序列就是黑框中显示的。

 

 

二.运算指令

  1. 加法指令:(i,l,f,d)add。
  2. 减法指令:(i,l,f,d)sub。
  3. 乘法指令:(i,l,f,d)mul。
  4. 除法指令:(i,l,f,d)div。
  5. 求余指令:(i,l,f,d)rem。

 

大家可能会好奇byte,short类型去哪里了,因为我们的字节码指令只能有256个,目前已经使用了200个左右,为了节省字节码的使用,一边于以后的扩展,所以就将byte,short当作int型处理了,这样就可以节省两个字节码指令。

 

字节码指令介绍——深入理解Java虚拟机

 源码如下:

public class Hello{
	
	public static void main(String[] args){
		int a=1;
		int b=2;
		int c=a*b;

		int d=2;
		int e=1;
		int f=d/e;

		int g=2;
		int h=1;
		int i=g+h;

		int j=2;
		int k=1;
		int l=j-k;
	}
}

三.类型转换指令

  1. 类型转换指令
  1. 宽化类型转换:就是小范围的类型向大范围的数转换。是安全的,不会造成数据丢失。
  2. 窄化类型转换:大范围类型向小范围型转换,也可以说是高精度类型向低精度类型转换,不安全,容易造成数据丢失。

 

     指令格式(类型首字母)2(类型首字母);如i2l就是int转换为long。

字节码指令介绍——深入理解Java虚拟机

 

 

四.对象创建与访问指令

  1. 创建类实例指令:new。
  2. 创建数组指令:newarray,anewarry,multianewarry。
  3. 访问字段(static字段,或者称为类变量)和实例字段(实例变量)的指令:getfield,putfield,getstatic,putstatic。
  4. 把一个数组元素加载到操作数栈的指令:baload,caload,saload,iaload,laload,faload,daload,aaload。(可以简单的认为前面的第一个字母是数组类型,第二个字母是array的首字母)。
  5. 将一个操作数栈的值存储到数组元素中的指令:bastore,castore,sastore,iastore,fastore,dastore,aastore(可以简单的认为前面的第一个字母是数组类型,第二个字母是array的首字母)。
  6. 取数组长度的指令:arraylengh。
  7. 查看类实例类型指令:instanceof,checkcast。

 

 

五.操作数栈管理指令

 

  1. 将操作数栈定一个或连个元素出栈:pop,pop2。
  2. 复制栈顶一个或者连个数值并将复制值或者双份的复制值压入栈顶:dup, dup2,dup_x1,dup2_x1,dup_x2,dup2_x2。
  3. 将栈最顶端的两个数值互换:swap。

 

 

六.控制转移指令。

 

控制转移指令可以让Java虚拟级有条件或者无条件的从指定位置指令而不是控制转移指令的下一条指令继续执行程序,从概念模型上垃圾,可以认为控制转移指令就是在有条件或者无条件的修改PC寄存器的值,控制转移指令如下:

 

1.条件分支: ifeq,iflt,ifle,ifne,ifgt,ifge,ifnull,ifnonnull,if_icmpeq,if_icmpne,if_icmpit,if_icmpgt,if_icmple,i f_icmpfe,if_acmpeq和if_acmpne。

2.复合条件分支:tableswitch,lookupswitch。

3.无条件分枝:goto,goto_w,jsr,jsr_w,ret。

 

七.方法调用和返回指令。

 

 

  1. invokevirtual指令用于调用对象的实例方法,根据对象的实例类型进行分派,这就是Java语言中最常见的分派方式。
  2. invokeinterface指令用于调用接口方法,它会在运行时搜索一下实现这个接口方法的对象,找出合适的方法进行调用。
  3. invokespecial指令用于调用一些需要特殊处理的实例方法包括实例初始化方法,私有方法和父方法。
  4. invokestatic指令用于调用类方法(static 方法)

 

方法调用指令与数据类型无关,而方法返回值指令是根据返回值区分的,包括ireturn(当返回值是boolean,byte,short,char和int类时使用),lreturn,freturn,dreturn,areturn,另外还有一条return指令供声明为void的方法,实例初始化方法及类和接口的类初始化方法使用。

 

八.异常处理指令

在Java程序显式的抛出异常操作(throw语句)都有athrow指令来实现,除了用throw语句显式的抛出之外,Java虚拟机规范还规定了许多运行时异常会在其他Java虚拟机指令检测到异常的情况下抛出。

 

注意:在Java虚拟机种处理异常(catch语句)不是由字节码指令来实现的(很久以前曾经使用jst和ret指令来实现,现在已经不用了),而时采用异常表来完成

 

 

 

写的不好,还望海涵。谢谢。

 

 

 

 

 

 

 

 

 

 

 

相关文章:

  • 2021-05-19
  • 2021-12-29
  • 2022-12-23
  • 2021-11-23
  • 2021-10-14
  • 2021-08-12
  • 2021-12-10
  • 2021-05-21
猜你喜欢
  • 2021-07-13
  • 2021-05-19
  • 2021-12-08
  • 2021-09-10
  • 2019-11-18
  • 2021-09-11
相关资源
相似解决方案