目录

一、概述

二、所有算术指令

三、彻底搞懂i++与++i的问题

四、比较指令的说明


一、概述

算术指令用于对两个操作数栈上的值进行某种特定运算,并把计算之后的结果重新压入操作数栈中。

【a】算术指令大体上可以分为两种:

  • 对整型数据进行运算的指令;
  • 对浮点类型数据进行运算的指令;

【b】byte、short、char和boolean类型说明

在每一大类中,都有针对Java虚拟机具体数据类型的专用算术指令。但没有直接支持byte、 short、 char和boolean类型的算术指令,对于这些数据的运算,都使用int类型的指令来处理。此外,在处理boolean、byte、short和char类型的数组时,也会转换为使用对应的int类型的字节码指令来处理。

【c】Java虚拟机中的实际类型与运算类型

实际类型

运算类型

boolean

int

byte

int

char

int

short

int

int

int

float

float

reference

reference

returnAddress

returnAddress

long

long

double

double

数据运算可能会导致溢出,例如两个很大的正整数相加,结果可能是一个负数。其实Java虚拟机规范并无明确规定过整型数据溢出的具体结果,仅规定了在处理整型数据时,只有除法指令以及求余指令中当出现除数为0时会导致虚拟机抛出算术运算异常ArithmeticException。

【d】运算模式

向最接近数舍入模式:JVM要求在进行浮点数计算时,所有的运算结果都必须舍入到适当的精度,非精确结果必须舍入为可被表示的最接近的精确值,如果有两种可表示的形式与该值一样接近,将优先选择最低有效位为零的;

向零舍入模式:将浮点数转换为整数时,采用该模式,该模式将在目标数值类型中选择一个最接近但是不大于原值的数字作为最精确的舍入结果;

【e】NaN值使用

当一个操作产生溢出时,将会使用有符号的无穷大表示,如果某个操作结果没有明确的数学定义的话,将会使用 NaN值来表示。而且所有使用NaN值作为操作数的算术操作,结果都会返回 NaN;

public void method1(){int i = 10;double j = i / 0.0;System.out.println(j);//无穷大double d1 = 0.0;double d2 = d1 / 0.0;System.out.println(d2);//NaN: not a number
}

二、所有算术指令

所有的算术指令包括:

加法指令:iadd、ladd、fadd、dadd

减法指令:isub、lsub、fsub、dsub

乘法指令:imul、lmul、fmul、dmul

除法指令:idiv、ldiv、fdiv、ddiv

求余指令:irem、lrem、frem、drem //remainder:余数

取反指令:ineg、lneg、fneg、dneg //negation:取反

自增指令:iinc

位运算指令,又可分为:

  • 位移指令:ishl、ishr、 iushr、lshl、lshr、 lushr
  • 按位或指令:ior、lor
  • 按位与指令:iand、land
  • 按位异或指令:ixor、lxor

比较指令: dcmpg、dcmpl、 fcmpg、fcmpl、lcmp

下面我们看一个示例:

public void method2() {float i = 10;float j = -i;i = -j;
}

查看其字节码解析即分析如下:

0 ldc #21 <10.0>   //将10压入操作数栈中
2 fstore_1   //将10存储在局部变量中索引为1的位置
3 fload_1    //将局部变量表中索引为1的值10重新压入操作数栈中
4 fneg       //对i取反
5 fstore_2   //将i取反的结果j存储在局部变量表中索引为2的位置
6 fload_2    //将局部变量表中索引为2的j变量的值重新压入操作数栈中
7 fneg       //对j取反
8 fstore_1   //将取反后的结果存入局部变量表中索引为1的位置
9 return     //方法返回

自增与相加运算示例一

public void method3(int j) {int i = 100;i = i + 10;
}

查看其字节码解析即分析如下:

0 bipush 100  //将100压入操作数栈中
2 istore_2    //将100存入局部变量表中索引为2的位置
3 iload_2     //局部变量表中索引为2的值100压入操作数栈中
4 bipush 10   //将10压入操作数栈中
6 iadd        //将操作数栈顶两个元素做相加操作,然后将计算后的110存入操作数栈中
7 istore_2    //将110存入局部变量表中索引为2的位置
8 return      //方法返回

图解分析如下:

自增与相加运算示例二

public void method4(int j) {int i = 100; i += 10;
}

查看其字节码解析即分析如下:

0 bipush 100     //将100压入操作数栈中
2 istore_2       //将100存入局部变量表中索引为2的位置
3 iinc 2 by 10   //将局部变量表中的值加上10,变成110
6 return         //方法返回

图解分析如下:

异或运算举例

public int method5(int i, int j) {return ((i + j - 1) & ~(j - 1));
}

查看其字节码解析即分析如下:

 0 iload_1          //局部变量表中索引为1的值压入操作数栈中1 iload_2          //局部变量表中索引为2的值压入操作数栈中2 iadd             //将操作数栈顶两个元素做相加操作,计算后的结果重新压入操作数栈中3 iconst_1         //将常量1压入操作数栈中4 isub             //做一个减法操作,计算后的结果重新压入操作数栈中5 iload_2          //局部变量表中索引为2的值压入操作数栈中6 iconst_1         //将常量1压入操作数栈中7 isub             //做一个减法操作,计算后的结果重新压入操作数栈中8 iconst_m1        //将常量-1压入操作数栈中9 ixor            //做一个异或操作
10 iand            //做一个与运算
11 ireturn         //方法返回

静态方法操作数栈中没有this:方法入参i占据了第0个索引

public static int method6(int i) {return ((i + 1) - 2) * 3 / 4;
}

假设调用method6(5)进行计算,那么此时字节码指令相关操作解析如下:

0 iload_0          //将局部变量表中索引为0位置上的值5压入操作数栈中
1 iconst_1         //将常量1压入操作数栈中
2 iadd             //将栈顶两个元素做一个相加操作,然后计算之后的值6重新压入操作数栈中
3 iconst_2         //将常量2压入操作数栈中
4 isub             //做一个减法操作,然后【6-2】= 4重新压入操作数栈中
5 iconst_3         //将常量3压入操作数栈中
6 imul             //做一个乘法操作,4 * 3 = 12,然后12存入操作数栈中
7 iconst_4         //将常量4压入操作数栈中
8 idiv             //做一个除法操作,12 /4 = 3,然后3存入操作数栈中
9 ireturn         //方法返回

图解分析如下:

查看jclasslib对应的局部变量表如下图所示:

三、彻底搞懂i++与++i的问题

直接看下面两个程序:

public void method6(){int i = 10;i++;
}
public void method7(){int i = 10;++i;
}

我们编译之后,发现上述两个程序的字节码信息是一样的,如下:

0 bipush 10        //将10压入操作数栈中
2 istore_1         //将10存到局部变量表中索引为1的位置
3 iinc 1 by 1      //将局部变量表中索引为1的值10,加1,变成11
6 return           //方法返回

我们可以看到,如果不涉及赋值操作,从字节码角度看i++和++i是一样的。

但是涉及到赋值操作时,字节码就有所区别了:

public void method7(){int i = 10;int a = i++;int j = 20;int b = ++j;
}

字节码信息解析如下:

0 bipush 10        //将10压入操作数栈中2 istore_1         //将10存入局部变量表中索引为1的位置【变量i】3 iload_1          //重新将局部变量表中索引为1的值10压入操作数栈中4 iinc 1 by 1      //将局部变量表中索引为1的值10做一个加1操作,变成117 istore_2         //将10存入局部变量表中索引为2的位置【变量a】,所以a = 108 bipush 20        //将20压入操作数栈中
10 istore_3         //将20存入局部变量表中索引为3的位置【变量j】
11 iinc 3 by 1      //将局部变量表中索引为3的值20做一个加1操作,变成21
14 iload_3          //将局部变量表中索引为3的值21压入操作数栈中
15 istore 4        //将21存入局部变量表中索引为4的位置【变量b】,所以b = 21
17 return          //方法返回

结合图解分析一下:

如上,我们可以总结出:

  • i++是先赋值后运算;
  • ++i是先运算后赋值;

四、比较指令的说明

  • 比较指令的作用是比较栈顶两个元素的大小,并将比较结果压入操作数栈中;
  • 比较指令有:dcmpg, dcmpl、fcmpg、fcmpl、lcmp;
    • 与前面讲解的指令类似,首字符d表示double类型,f表示float,l表示long;
  • 对于double和float类型的数字,由于NaN的存在,各有两个版本的比较指令。以float为例,有fcmpg和fcmpl两个指令,它们的区别在于在数字比较时,若遇到NaN值,处理结果不同;
  • 指令lcmp针对long型整数,由于long型整数没有NaN值,故无需准备两套指令

举例:

指令fcmpg和fcmpl都从栈中弹出两个操作数,并将它们做比较,设栈顶的元素为v2,栈顶顺位第2位的元素为v1,若v1=v2,则压入0;若v1 > v2则压入1;若v1 < v2则压入 -1。

两个指令的不同之处在于,如果遇到NaN值,fcmpg会压入1,而fcmpl会压入-1。

字节码指令之算术指令相关推荐

  1. 深入理解JVM虚拟机(五):字节码指令简介

    Java 虚拟机的指令由一个字节长度的.代表着某种特定操作含义的数字(称为操作码)以及跟随其后的零至多个代表此操作所需参数(操作数)而构成.由于 Java 虚拟机采用面向操作数栈而不是寄存器的架构,所 ...

  2. Java虚拟机字节码指令概述

    虚拟机字节码指令 Java虚拟机的指令由一个字节长度的.代表着某种特定操作含义的数字(称为操作码,Opcode)以及跟随其后的零至多个代表此操作所需参数(称为操作数,Operands)而构成. 一 基 ...

  3. jvm理论-字节码指令

    Java虚拟机的指令由一个字节长度的.代表着某种特定操作含义的数字(称为操作码,Opcode)以及跟随其后的零至多个代表此操作所需参数(称为操作数,Operands)而构成. 基本数据类型 1.除了l ...

  4. java字节码指令简介(仅了解)

    [0]README 0.1)本文全文转自 "深入理解jvm", 旨在了解 java字节码指令 的基础知识: [1]写在前面 1)由于jvm 采用面向操作数栈而不是寄存器的结构,所以 ...

  5. Java字节码指令简介

    本文是<深入理解Java虚拟机>中第六章的读书笔记. 1.概述 在Class文件中,Java方法里的方法体,也就是代表着一个Java源码程序中程序的部分存储在方法表集合的Code属性中.存 ...

  6. Java的Class类文件结构及基本字节码指令

    Class类文件的结构 概念:Class文件是一组以8位字节为基础单位的二进制流 按顺序整齐排列 没有任何分隔符,内容全部是运行时的必要数据,没有空隙存在 排序方式:高位在前 Big-Endian:最 ...

  7. JVM004_字节码指令简介

    字节码指令简介 Java虚拟机指令由操作码(Opcode)和跟随其后的零至多个操作数(Operand)组成. 操作码:一个字节长度的,代表某种特定操作含义的数字. 操作数:操作码需要的参数. 字节码与 ...

  8. java中用于运行字节码的命令_Java字节码指令

    1. 简介 Java虚拟机的指令由一个字节长度的.代表着某种特定操作含义的数字(称为操作码)以及跟随其后的零至多个代表此操作所需参数(称为操作数)而构成. 由于Java虚拟机采用面向操作数栈而不是寄存 ...

  9. 【深入理解java虚拟机】 - JVM字节码指令介绍

    文章目录 什么是字节码指令 javap的用法 字节码与数据类型 字节码指令集 加载和存储指令 运算指令 类型转换指令 对象创建与访问指令 操作数栈管理指令 控制转移指令 方法调用和返回指令 异常处理指 ...

  10. class 类文件结构与字节码指令

    JVM执行子系统 一.Class 类文件结构 1.Java跨平台的基础 各种不同平台的虚拟机与所有平台都统一使用的程序存储格式--字节码(ByteCode)是构成平台无关性的基石,也是语言无关性的基础 ...

最新文章

  1. 【无标题】ubuntu20.04 开机引导后黑屏 光标闪现 无法进入图形桌面的解决方案_Denis.Zzzzzzzz?的博客-CSDN博客_ubuntu20黑屏光标闪烁
  2. JAVA的知识点4——字符型变量/常量 boolean类型变量/常量
  3. 【控制】《复杂运动体系统的分布式协同控制与优化》-方浩老师-第1章-绪论
  4. 复制Hadoop目录至其他节点时的注意点
  5. js学习总结----弹性势能动画之抛物线运动
  6. 【酷熊科技】工作积累 ----------- 在unity3d里怎样隐藏物体
  7. 【原创】系统分析师--任重而道远
  8. 转:用ASP.NET创建网络相册
  9. 计算机屏幕很暗怎么办,笔记本屏幕变暗,详细教您笔记本屏幕变暗怎么办
  10. android 本地日历,Android日历提供商:如何删除自己的本地日历?
  11. 人赚钱多少的本质区别在于:出售自己时间的方法不同
  12. 主题模型TopicModel:LDA中的数学模型
  13. junsansi 列表(4) - 三思笔记,ORACLE学习轨迹~~~~ - ITPUB个...
  14. 办公自动化系统项目报告
  15. Jmeter脚本录制
  16. python卸载库命令_python常用删除库的方法
  17. 超出本地计算机网络,超出本地计算机网络适配器卡的名称限制怎么解决?
  18. 电力行业适合学习的开源软件
  19. 网站用户活跃度统计服务器,活跃用户统计规则
  20. ajax的state,ajax 中readystate一直为1

热门文章

  1. 信雅达银行外包怎么样_光大银行信用卡逾期2年3万会坐牢吗?信用卡逾期半年要起诉...
  2. 自动驾驶 5-3 前馈速度控制 Feedforward Speed Control
  3. mac sublime text 3 列操作,替换相同内容, 用动态输入的方式
  4. java中如何访问类中的字段_java – 在子类中使用super关键字访问超类私有字段
  5. 2021-09-03DIEN分成两步去抓取用户的兴趣演化:1兴趣抽取层 去抽取基于用户行为序列的兴趣序列2兴趣演化层 跟target item相关
  6. 多元线性回归实现代码
  7. 多小区下小区上行速率的计算(2)
  8. modelsim安装_XLINUXFPGA开发工具篇modelsim的安装
  9. 在linux服务器上安装sublime编辑器
  10. 使用 LaTeX 语言对 MATLAB 中的图片进行标注