简介 

  Java虚拟机的指令由一个字节长度的、代表着某种特定操作含义操作码(opcode)以及跟随其后的零至多个代表此操作所需参数的操作数(operand)所构成
  虚拟机中许多指令并不包含操作数.只有一个操作码。
  如果忽略异常处理,执行逻辑类似

do{
  自动计算pc寄存器以及从pc寄存器的位置取出操作码;
  if(存在操作数){
    取出操作数;
  }
  执行操作码所定义的操作;
}while(处理下一次循环);

  操作数的数量以及长度取决于操作码,如果一个操作数的长度超过了一个字节,那么它将大端排序存储,即高位在前的字节序。限制了操作码长度为一个字节 0~255,   但是也就导致操作码个数不能超过256。

  例如,如果要将一个16位长度的无符号整数使用两个无符号字节存储起来(将它们命名为byte]和byte2 ),那这个16位无符号整数的值就是:  (bytel<<8) | byte2.

  字节码指令流应当都是单字节对齐的,只有,tableswitch和lookupswitch两个指令例外 这俩货是4字节为单位的。
  
  如果向上面那样如果操作码处理超过一个字节的数据时,就必须在运行时从字节流中重建出具体数据结构,将会有一定程度的性能损失
  但是优势也是很明显的:
    1.放弃编译后代码的操作数对齐 也就省略很多填充和间隔符号;

    2.限制长度和放弃对齐也尽可能的让编译后的代码短小精干。
 
字节码与数据类型
    在Java虚拟机指令集中,大多数的指令都包含了其操作所对应的数据类型信息。例如,iload 指令用于从局部变量表中加载int 型的数据到操作数栈中。

    但是由于虚拟机操作码长度只有一个字节,所以包含了数据类型的操作码就为指令集的设计带来了很大的压力:

    如果每一种数据类型相关的指令都支持Java虚拟机所有运行时数据类型的话,那指令集的数据就会超过256个了。因此虚拟机只提供了有限的指令集来支持所有的数据类型。


    JVM共有9种基本类型,对于基本类型  指令在设计的时候都用一个字母缩写来指代(boolean除外)。

      

    当然也有一些并没有明确字母指代数据类型,比如arraylength 指令,并没有代表数据类型的特殊字符,操作数只能是一个数组类型的对象,另外还有一些,比如无条件跳转指令goto 则是与数据类型无关的。


指令-相关计算机英语词汇含义


指令-数据类型相关的指令

    从上表的空白处可以看得出来:大部分数据类型相关联的指令,都没有支持整数类型 byte char short ,而且没有任何指令支持boolean类型。

    因为编译器会在编译期或者运行期  将byteshort 类型的数据 带符号扩展 为相应的int类型数据。

    类似的,booleanchar类型数据零位扩展为相应的int类型数据,在处理boolean byte short char类型的数组时,也会转换为使用对应的int类型的字节码指令来处理


 按照逻辑功能进行划分

 加载存储指令

  加载和存储指令用于将数据栈帧中局部变量表操作数栈之间来回传输。

  这类指令包括如下内容:

    1. 将一个局部变量加载到操作栈:iload、iload_<n>、lload、lload_<n>、fload、fload<n>、dload、dload<n>、aload、aload<n>   

    2. 将一个数值从操作数栈存储到局部变量表:istore、istore_<n>、lstore、lstore_<n>、fstore、fstore_<n>、dstore、dstore_<n>、astore、astore_<n>

    3. 将一个常量加载到操作数栈:bipush、sipush、ldc、ldc_w、ldc2_w、aconst_null、iconst_m1、iconst_<i>、lconst_<l>、fconst_<f>、dconst_<d>

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

  存储数据的操作数栈和局部变量表主要就是由加载和存储指令进行操作的,除此之外,还有少量指令,如访问对象的字段或数组元素的指令也会向操作数栈运输数据。


运算指令

  运算或算术指令用于对两个操作数栈上的值进行某种特定运算,并把结果重新存入操作数栈顶

  算术指令分为两种:

    1. 整型运算的指令。

    2. 浮点型运算的指令。

  无论是哪种算术指令,都使用Java虚拟机的数据类型,由于没有直接支持byte、short、char和boolean类型的算术指令,使用操作int类型的指令代替。

    加法指令:iadd、ladd、fadd、dadd
    减法指令:isub、lsub、fsub、dsub
    乘法指令:imul、lmul、fmul、dmul
    除法指令:idiv、ldiv、fdiv、ddiv
    求余指令:irem、lrem、frem、drem
    取反指令:ineg、lneg、fneg、dneg
    位移指令:ishl、ishr、iushr、lshl、lshr、lushr
    按位或指令:ior、lor
    按位与指令:iand、land
    按位异或指令:ixor、lxor
    局部变量自增指令:iinc
    比较指令:dcmpg、dcmpl、fcmpg、fcmpl、lcmp


类型转换指令
   类型转换指令可以将两种不同的数值类型进行相互转换。这些转换操作一般用于实现用户代码中的显式类型转换操作,或者用来解决字节码指令集不完备的问题。

   因为数据类型相关指令无法与数据类型一一对应的问题,比如byte short char boolean使用int,   所以必须要转换。

  分为宽化窄化含义如字面含义,存储长度的变宽或者变窄。宽化也就是常说的安全转换,不会因为超过目标类型最大值丢失信息。窄化则意味着很可能会丢失信息。

   宽化指令

  • int类型到long、float或者double类型 (i2l、i2f、i2d)
  • long类型到float、double类型(l2f 、l2d)
  • float类型到double类型(f2d)

  窄化指令

  • int类型到byte short char类型(i2b 、i2s 、i2c)
  • long类型到int类型(l2i)
  • float类型到int或者long类型(f2i 、f2l)
  • double类型到int long 或者float类型(d2i 、d2l 、d2f)

对象创建与访问指令

  类实例和数组都是对象。但是Java虚拟机对类实例和数组的创建使用了不同的字节码指令。

    指令如下:

  1. 创建类实例 : new。

  2. 创建数组的指令 :

    newarray  分配数据成员类型为基本数据类型的新数组
    anewarray  分配数据成员类型为引用类型的新数组
    multianewarray   分配新的多维数组

  3. 访问类字段(static字段,或者称为类变量)和实例字段(非static字段,或者称为实例字段)的指令:

    getstatic 从类中获取静态字段
    putstatic 设置类中静态字段的值
    getfield 从对象中获取字段值
    putfield 设置对象中的字段的值

  4. 把一个数组元素加载到操作数栈的指令:baload  caload  saload  iaload  laload  faload  daload  aaload

   5. 把一个操作数栈的值存储到数组元素中的指令:bastore  castore  sastore  iastore  lastore  fastore  dastore  aastor

   6. 取数组长度的指令:arraylength

   7. 检查类实例或者数组类型的指令:instanceof  checkcast


操作数栈管理指令

    操作数栈管理指令,顾名思义就是直接用于管理操作栈的,操作数栈的直接操作主要有 出栈/复制栈顶元素 / 以及 交换栈顶元素。

    如同操作应普通数据结构中的堆栈那样,java虚拟机提供了一些用于直接操作数栈的指令,包括:

    1. 将操作数栈的栈顶一个或两个元素出栈:pop、pop2。

    2. 复制栈顶一个或两个数值并将复制值或双份的复制值重新压入栈顶:dup、dup2、dup_x1、dup2_x1、dup_x2、dup2_x2.

    3. 将栈最顶端的两个数值互换:swap。


控制转移指令

  控制转移指令可以让Java虚拟机有条件或者无条件的从指定的位置指令继续执行程序,而不是当前控制转移指令的下一条。

控制指令如下:

  1. 条件分支

ifeq  当栈顶int类型元素    等于0时    ,跳转
ifne 当栈顶int类型元素 不等于0 时,跳转
iflt 当栈顶int类型元素 小于0 时,跳转
ifle 当栈顶int类型元素 小于等于0 时,跳转
ifgt 当栈顶int类型元素 大于0 时,跳转
ifge 当栈顶int类型元素 大于等于0 时,跳转
if_icmpeq 比较栈顶两个int类型数值的大小 ,当前者 等于 后者时,跳转
if_icmpne 比较栈顶两个int类型数值的大小 ,当前者 不等于 后者时,跳转
if_icmplt 比较栈顶两个int类型数值的大小 ,当前者 小于 后者时,跳转
if_icmple 比较栈顶两个int类型数值的大小 ,当前者 小于等于 后者时,跳转
if_icmpge 比较栈顶两个int类型数值的大小 ,当前者 大于等于 后者时,跳转
if_icmpgt 比较栈顶两个int类型数值的大小 ,当前者 大于 后者时,跳转
if_acmpeq 比较栈顶两个引用类型数值的大小 ,当前者 等于 后者时,跳转
if_acmpne 比较栈顶两个引用类型数值的大小 ,当前者 不等于 后者时,跳转

  2.复合条件分支

tableswitch    switch 条件跳转 case值连续
lookupswitch switch 条件跳转 case值不连续

  3.无条件分支 

goto    无条件跳转
goto_w 无条件跳转 宽索引
jsr SE6之前 finally字句使用 跳转到指定16位的offset,并将jsr下一条指令地址压入栈顶
jsr_w SE6之前 同上 宽索引
ret SE6之前返回由指定的局部变量所给出的指令地址(一般配合jsr jsr_w使用)
w 同局部变量的宽索引含

方法调用和返回指令

  方法调用分为:实例方法,接口方法,调用父类私有实例,初始化等特殊方法,类静态方法等

以下5条指令用于方法调用:
invokevirtual指令用于调用对象的实例方法
invokeinterface指令用于调用接口方法,它会在运行时搜索由特定对象所实现的这个接口方法,并找出适合的方法进行调用。
invokespecial指令用于调用一些需要特殊处理的实例方法,包括实例初始化方法、私有方法和父类方法。
invokestatic指令用于调用类方法(static方法)
invokedynamic 调用动态链接方法

异常处理指令

  Java程序中显式抛出异常的操作  throw语句,都是由athrow 指令来实现的,除了throw语句显式的抛出异常情况之外,

  Java虚拟机规范还规定了许多运行时异常,会在其他Java虚拟机指令检测到异常情况时,自动抛出。


同步指令

  java虚拟机可以支持方法级的同步和方法内部一段指令序列的同步,这两种同步结构都是使用管程(Monitor)来支持的。

  同步一段指令集序列通常是由Java语言中的synchronized 语句块来表示的,

  Java虚拟机的指令集中有monitorenter  monitorexit  (monitor  +enter/exit)

指令小结:

加载存储指令 将数据在栈帧中的局部变量表和操作数栈之间来回传输。
运算指令 用于对两个操作数栈上的值进行某种特定运算,并把结果重新存入到操作数栈顶。
类型转换指令 将两种不同的数值类型进行相互转换。
对象创建和访问指令 ....
操作数栈管理指令 用于管理操作栈的,操作数栈的直接操作主要有 出栈/复制栈顶元素 / 以及 交换栈顶元素。
控制转移指令 让Java虚拟机有条件或者无条件的从指定的位置指令继续执行程序,而不是当前控制转移指令的下一条。条件分支、复合条件分支、无条件分支
方法调用和返回指令  
异常处理指令  
同步指令  

JVM —— 类文件结构(下)的更多相关文章

  1. JVM类文件结构

    作为一名Java后台开发的程序员, 深入理解JVM, 重要性不言而喻, 这篇文章主要是记录JVM类文件结构相关知识. 2. 实例 这部分比较抽象, 所以以实例的形式来学习. 这部分作为资料, 以便后面 ...

  2. jvm 类文件结构学习

    本文以代码示例来学习 java 类文件的结构,其中对类文件结构的学习均来自周志明先生所著的 <深入理解 Java 虚拟机>一书,在此表示诚挚的感谢. 代码如下: package com.r ...

  3. 【搞定Jvm面试】 面试官:谈谈 JVM 类文件结构的认识

    类文件结构 一 概述 在 Java 中,JVM 可以理解的代码就叫做字节码(即扩展名为 .class 的文件),它不面向任何特定的处理器,只面向虚拟机.Java 语言通过字节码的方式,在一定程度上解决 ...

  4. 四、JVM — 类文件结构

    类文件结构 一 概述 二 Class 文件结构总结 2.1 魔数 2.2 Class 文件版本 2.3 常量池 2.4 访问标志 2.5 当前类索引,父类索引与接口索引集合 2.6 字段表集合 2.7 ...

  5. JVM —— 类文件结构(上)

    一.概述 实现语言无关性的基础仍然是虚拟机和字节码存储格式.java虚拟机不和包括java在内的任何语言绑定,它只与“Class文件”这种特定的二进制文件格式所关联,Class文件中包含了java虚拟 ...

  6. JVM学习笔记(三):类文件结构

    代码编译的结果从本地机器码转变为字节码,是存储格式发展的一小步,却是编程语言发展的一大步. 实现语言无关性的基础是虚拟机和字节码存储格式.Java虚拟机不和包括Java在内的任何语言绑定,只与&quo ...

  7. JVM学习第三天(JVM的执行子系统)之开篇Class类文件结构

    虽然这几天 很忙,但是学习是不能落下的,也不能推迟,因为如果推迟了一次,那么就会有无数次;加油,come on! Java跨平台的基础: 各种不同平台的虚拟机与所有平台都统一使用的程序存储格式——字节 ...

  8. JVM学习笔记-第六章-类文件结构

    JVM学习笔记-第六章-类文件结构 6.3 Class类文件的结构 本章中,笔者只是通俗地将任意一个有效的类或接口锁应当满足的格式称为"Class文件格式",实际上它完全不需要以磁 ...

  9. JVM笔记9-Class类文件结构

    1.Class类文件结构  Class 文件是一组以 8 位字节为基础单位的二进制流,各个数据项目严格按照顺序紧凑地排列在 Class 文件之中,中间没有添加任何分隔符,这使得整个 Class 文件中 ...

随机推荐

  1. [线性代数] 线性子空间入門 Basic Vector Subspaces

    导语:其他集数可在[线性代数]标籤文章找到.线性子空间是一个大课题,这里先提供一个简单的入门,承接先前关于矩阵代数的讨论,期待与你的交流. Overview: Subspace definition ...

  2. ROUND() 函数

    ROUND() 函数 ROUND 函数用于把数值字段舍入为指定的小数位数. SQL ROUND() 语法 SELECT ROUND(column_name,decimals) FROM table_n ...

  3. 解析复杂的嵌套数据结构-jsonpath

    JsonPath是一种简单的方法来提取给定JSON文档的部分内容. JsonPath有许多编程语言,如Javascript,Python和PHP,Java. JsonPath提供的json解析非常强大 ...

  4. Git .gitignore中已添加文件路径,但仍未被忽略

    当文件之前已经被提交到仓库后,后面即使将文件路径添加到 .gitignore ,使用 git status 命令,依然会看到文件被修改. $ git status 位于分支 master 您的分支与上 ...

  5. Java 面向对象(四)

    代码块 什么是代码块 在类中或方法当中 使用 { } 括起来的一段代码,就称它是一个代码块. 在代码块当中定义的变量我们称是局部变量,在外面是没有办法使用的.这里定义的 a 就是一个局部变量. 代码块 ...

  6. 服务器(Linux)上运行python总结

    跑实验换了几次服务器了,每次遇到相似问题都要重新百度,而且每次百度搜索出的顺序都不一样,又得重新找半天,这次把遇到的问题都总结一下. 1.准备 PuTTY和FileZilla FileZilla使用F ...

  7. 【Python】把文件名命名成canlendar.py竟然导致无法使用canlendar模块 附赠2020年月历

    这个bug困扰了我一阵,直到在 http://www.codingke.com/question/15489 找到了解决问题的钥匙,真是没想到居然是这个原因导致的. 下面是出错信息,可以看到只要目录下 ...

  8. SOCKET_CONNECT_TIMEOUT is the timeout for the connection to be established and SOCKET_TIMEOUT

    https://github.com/niwinz/django-redis/blob/master/doc/content.adoc#32-socket-timeout 3.2. Socket ti ...

  9. iptables+ipset自动封闭和解封频繁访问web服务的恶意IP

    转载于互联网     iptables直接针对ip进行封禁,在ip数量不大的时候是没什么问题的,但当有大量ip的时候性能会严重下降,iptables是O(N)的性能.而ipset就像一个集合,把需要封 ...

  10. 【分类算法】感知机(Perceptron)

    0 - 算法描述 感知机算法是一类二分类算法,其问题描述为,给定一个训练数据集 $$T=\{(x_1,y_1),(x_2,y_2),\cdots,(x_N,y_N)\},$$ 其中$x_i\in \m ...