虚拟机类加载机制(三)--- 字节码指令

Java的技术体系包括

  • 支持Java程序运行的虚拟机(JVM)
  • 提供接口支持的Java API
  • Java 编程语言
  • 第三方Java框架(如Spring等)

代码编译的结果从本地机器码转变为字节码,是存储格式发展的一小步,确实编程语言的一大步。


java代码经过编译器编译后,类中方法体内的代码逻辑会被编译为一行行虚拟机能够识别的指令,而java虚拟机规范中通过一个字节来存储所有的虚拟机指令,因此我们将这种指令集称之为字节码指令。

字节码指令的含义

字节码指令是指,由一个字节长度来表示,代表着特定含义的数字(称之为操作码)所构成的指令,其后有可能跟随一个或者多个此命令所需要的参数(称之为操作数)。大多数的字节码指令都是不包含操作数的,只存在一个操作码。

字节码指令的优势在于,由于不需要操作数长度对齐,所以可以节省很多用于操作数对齐而带来的填充和间隔符号所占用的空间。同时一个字节的长度来代表操作码,从设计的初衷我们就能看出视为了尽可能获得短小的编译代码。而这样的设计初衷也是由java语言为了尽可能追求在小数据量和高传输效率的场景中发挥优势所决定的。

它的劣势同样十分明显,由于一个字节所能代表的取值范围为0~255,这就意味着操作码最多只能有256个。同时由于没有操作数长度对齐,当操作码需要处理的数据大于一个字节的长度的时候,比如要存储一个16位长度的数据,那需要两个字节来存储,并且必须在运行时通过某种规则还原出原始的数据。这样的操作会导致字节码在执行的时候必然会损耗一些性能。

分类

字节码指令按照用途,大致可以分为9类

  1. 加载和存储指令
  2. 运算指令
  3. 类型转换指令
  4. 对象创建与访问指令
  5. 操作数栈管理指令
  6. 控制转移指令
  7. 方法调用和返回指令
  8. 异常处理指令
  9. 同步指令

加载和存储指令

加载和存储指令用于将数据在栈帧中的局部变量表和操作数栈之间来回传输,这些指令包括如下

  • 将一个局部变量加载到操作数栈:iload, iload_<n>, lload, lload<n>, fload, fload_<n>, dload, dload_<n>, aload, aload_<n>
  • 将一个数值从操作数栈存储到局部变量表:istore, istore_<n>, lstore, lstore_<n>, fstore, fstore_<n>, dstore, dstore_<n>, astore, astore_<n>
  • 将一个常量加载到操作数栈:bipush, sipush, ldc, ldc_w, ldc2_w, aconst_null, iconst_ml, iconst_<i>, lconst_<l>, fconst_<f>, dconst_<d>
  • 扩充局部变量表的访问索引的指令:wide

运算指令

运算指令用于将两个操作数栈上的值进行某种特定的运算,并把结果重新存入到操作数栈顶。算数指令大体可以分为两种:对整型数据进行运算的指令,和对浮点型数据进行运算的指令。

  • 加法指令: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

虚拟机规范中规定,在运算指令时,只有在除法指令或者求余指令中当除数为0的时候,会抛出ArithmeticException,其余任何场景即使出现溢出,也不会产生任何运行时异常

类型转换指令

类型转换指令可以将两种不同数值类进行相互转换。java虚拟机直接支持以下类型的宽化类型转换

  • int -> long float double
  • long -> folat double
  • float -> double
    对于处理窄化类型转换时,必须显式地使用转换指令来完成。这些指令包括:i2b, i2c, i2s, f2i, f2l, d2i, d2l, d2f。数据类型窄化处理可能会出现溢出和精度丢失等问题,但是java虚拟机规范中规定,数值窄化处理不会抛出任何运行时异常。

对象创建与访问指令

虽然在java语言层面,类实例和数组都是对象,但是虚拟机对类实例和数组的创建和访问使用的是不同的指令,这是因为他们的创建过程是不同的。指令如下

  • 创建类实例的指令:new
  • 创建数组是的指令:newarray, anewarray,multianewarray
  • 访问类变量和实例变量:getfield, putfield, getstatic, putstatic
  • 把一个数组元素加载到操作数栈的指令:baload, caload, saload, iaload, laload, faload, daload, aaload
  • 把一个操作数栈的值存储到数组元素中的指令:bastore, castore, sastore, iastore, fastore, dastore, aastore
  • 取数组长度指令:arraylength
  • 检查类实例类型的指令:instanceof, checkcast

操作数栈管理指令

  • 将操作数栈的栈顶一个或者两个元素出栈:pop, pop2
  • 复制栈顶一个或者两个数值,并将复制值或双份的复制值重新压入栈顶:dup, dup2, dup_1, dup2_1, dup_2, dup2_2
  • 将栈最顶端的两个数值互换:swap

控制转移指令

控制转移指令可以让虚拟机有条件或者无条件地从指定位置继续执行程序。指令如下

  • 条件分支:ifeq, iflt, ifle, ifgt, ifge, ifnull, ifnonnull, if_icmpeq, if_icmpne, if_icmplt, if_icmpgt, if_icacmpeq, if_acmpne
  • 复合条件分支:tableswitch, lookupswitch
  • 无条件分支:goto, goto_w, jsr, jsr_w, ret

方法调用和返回指令

  • invokevirtual, 用于调用对象的实例方法,根据对象的实际类型进行分派
  • invokeinterface, 用于调用接口方法,它会在运行时搜索一个实现了这个接口方法的对象,找出适合的方法进行调用
  • invokespecial, 用于调用一些需要特殊处理的实例方法,包括实例初始化方法、私有方法、父类方法
  • invokestatic, 用于调用类方法(static方法)
  • invokedynamic, 用于在运行时动态解析出调用点限定符所引用的方法,并执行该方法。
  • 方法返回指令:return, ireturn, lreturn, freturn, dreturn, areturn

异常处理指令

java程序中,显式抛出异常的操作都是由athrow指令来实现。除此之外,虚拟机规范还规定了许多运行时异常会由虚拟机自动抛出。

同步指令

java虚拟机可以支持方法级的同步和方法内部一段指令序列的同步,都是使用管程(Monitor)来支持。方法级的同步是隐式的,无需通过字节码指令来控制,因为通过方法常量池的方法表结构中的 ACC_SYNCHRONIZED 访问标志可以得知一个方法是否声明为同步方法。方法内部的同步则是通过 monitorenter 和 monitorexit 两条指令来完成。编译器需要确保,方法中调用过的每条 monitorenter 指令都必须执行其对应的 monitorexit指令。

总结

Java虚拟机规范规定了Java虚拟机应共同遵守的存储格式:Class文件格式字节码指令集。Class文件结构从虚拟机规范发布以来,java的技术体系发生了很多很多的变化,包括语言,API等有了极大的发展和很多的变化。但Class文件结构一直处于比较稳定的状态,它的主体结构、字节码指令都几乎没有什么大的变化。这也说明了反应出虚拟机的具体实现和上层的使用之间的耦合性很低,灵活性很大。

虚拟机加类加载机制的前三篇文章,详细阐述了Class文件的组成部分,每部分的含义,结构,以及使用方法。因为它是虚拟机执行引擎的数据入口,也是Java技术体系最重要的基础构成之一。

Class文件格式所具备的平台中立紧凑稳定可扩展的特点,是Java技术体系能够实现平台无关和语言无关的重要支柱

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 159,716评论 4 364
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 67,558评论 1 294
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 109,431评论 0 244
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 44,127评论 0 209
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 52,511评论 3 287
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 40,692评论 1 222
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 31,915评论 2 313
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 30,664评论 0 202
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 34,412评论 1 246
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 30,616评论 2 245
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 32,105评论 1 260
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 28,424评论 2 254
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 33,098评论 3 238
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 26,096评论 0 8
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 26,869评论 0 197
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 35,748评论 2 276
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 35,641评论 2 271

推荐阅读更多精彩内容