JVM深入理解(二)

JVM相关知识详解

一、Java虚拟机指令集

Java虚拟机指令由一个字节长度的、代表某种特定含义的操作码(Opcode)以及其后的零个至多个代表此操作参数的操作数构成。虚拟机中许多指令并不包含操作数,只有一个操作码。若忽略异常,JVM解释器使用一下为代码即可有效工作。

do{

自动计算PC寄存器以及从PC寄存器的位置取出操作码

if(存在操作数) 取出操作数;

执行操作码所定义的操作;

}while(处理下一次循环)

操作数的数量以及长度,取决于操作码,若一个操作数长度超过了一个字节,将会以Big-Endian顺序存储(高位在前字节码),其值应为(byte1<<8)|byte2。

字节码指令流是单字节对齐,只有"tableswitch"和"lookupswitch"两指令例外,它们的操作数比较特殊,以4字节为界限划分的,需要预留出相应的空位来实现对齐。

限制Java虚拟机操作码的长度为一个字节,且放弃编译后代码的参数长度对齐,是为了获得短小精干的编译代码,即使可能会让JVM实现付出一定性能成本为代价。由于操作码只能有一个字节长度,故限制了指令集的数量,又没有假设数据是对齐好的,意味着数据超过一个字节时,不得不从字节中重建出具体的数据结构,会损失一些性能。

数据类型与Java虚拟机

在JVM中的指令集中,大多数指令包含了其操作对应的数据类型信息。如iload指令从局部变量表中加载int型的数据到操作数栈中,而fload加载的是float类型的数据。

对于大部分与数据类型相关的字节码指令,他们的操作码助记符都有特殊的字符来表明:i代表int类型,l代表long,s代表short,b代表 byte,c代表char,f代表float,d代表double,a代表reference。有一些单独指令可以在必要的时候用来将一些不不支持的类型转换为可被支持的类型。

加载和存储指令

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

1)将一个局部变量加载到操作数栈的指令包括:iload,iload_,lload、lload_、float、 fload_、dload、dload_,aload、aload_。

2)将一个数值从操作数栈存储到局部变量标的指令:istore,istore_,lstore,lstore_,fstore,fstore_,dstore,dstore_,astore,astore_

3)将常量加载到操作数栈的指令:bipush,sipush,ldc,ldc_w,ldc2_w,aconst_null,iconst_ml,iconst_,lconst_,fconst_,dconst_

4)局部变量表的访问索引指令:wide

一部分以尖括号结尾的指令代表了一组指令,如iload_,代表了iload_0,iload_1等,这几组指令都是带有一个操作数的通用指令。

运算指令

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

1)加法指令:iadd,ladd,fadd,dadd

2)减法指令:isub,lsub,fsub,dsub

3)乘法指令:imul,lmul,fmul,dmul

4)除法指令:idiv,ldiv,fdiv,ddiv

5)求余指令:irem,lrem,frem,drem

6)取反指令:ineg,leng,fneg,dneg

7)位移指令:ishl,ishr,iushr,lshl,lshr,lushr

8)按位或指令:ior,lor

9)按位与指令:iand,land

10)按位异或指令:ixor,lxor

11)局部变量自增指令:iinc

12)比较指令:dcmpg,dcmpl,fcmpg,fcmpl,lcmp

Java虚拟机没有明确规定整型数据溢出的情况,但规定了处理整型数据时,只有除法和求余指令出现除数为0时会导致虚拟机抛出异常。

加载和存储指令

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

1)将一个局部变量加载到操作数栈的指令包括:iload,iload_,lload、lload_、float、 fload_、dload、dload_,aload、aload_。

2)将一个数值从操作数栈存储到局部变量标的指令:istore,istore_,lstore,lstore_,fstore,fstore_,dstore,dstore_,astore,astore_

3)将常量加载到操作数栈的指令:bipush,sipush,ldc,ldc_w,ldc2_w,aconst_null,iconst_ml,iconst_,lconst_,fconst_,dconst_

4)局部变量表的访问索引指令:wide

一部分以尖括号结尾的指令代表了一组指令,如iload_,代表了iload_0,iload_1等,这几组指令都是带有一个操作数的通用指令。

运算指令

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

1)加法指令:iadd,ladd,fadd,dadd

2)减法指令:isub,lsub,fsub,dsub

3)乘法指令:imul,lmul,fmul,dmul

4)除法指令:idiv,ldiv,fdiv,ddiv

5)求余指令:irem,lrem,frem,drem

6)取反指令:ineg,leng,fneg,dneg

7)位移指令:ishl,ishr,iushr,lshl,lshr,lushr

8)按位或指令:ior,lor

9)按位与指令:iand,land

10)按位异或指令:ixor,lxor

11)局部变量自增指令:iinc

12)比较指令:dcmpg,dcmpl,fcmpg,fcmpl,lcmp

Java虚拟机没有明确规定整型数据溢出的情况,但规定了处理整型数据时,只有除法和求余指令出现除数为0时会导致虚拟机抛出异常。

类型转换指令

类型转换指令将两种Java虚拟机数值类型相互转换,这些操作一般用于实现用户代码的显式类型转换操作。

JVM支持宽化类型转换(小范围类型向大范围类型转换):

1)int类型到long,float,double类型

2)long类型到float,double类型

3)float到double类型

窄花类型转换指令:i2b,i2c,i2s,l2i,f2i,f2l,d2l和d2f,窄化类型转换可能会导致转换结果产生不同的正负号,不同数量级,转换过程可能会导致数值丢失精度。如int或long类型转化整数类型T时,转换过程是仅仅丢弃最低位N个字节意外的内容(N是类型T的数据类型长度)

对象创建与操作

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

1)创建实例的指令:new

2)创建数组的指令:newarray,anewarray,multianewarray

3)访问字段指令:getfield,putfield,getstatic,putstatic

4)把数组元素加载到操作数栈指令:baload,caload,saload,iaload,laload,faload,daload,aaload

5)将操作数栈的数值存储到数组元素中执行:bastore,castore,castore,sastore,iastore,fastore,dastore,aastore

6)取数组长度指令:arraylength

7)检查实例类型指令:instanceof,checkcast

操作数栈管理指令

直接操作操作数栈的指令:pop,pop2,dup,dup2,dup_x1,dup2_x1,dup_x2,dup2_x2和swap

控制转移指令

让JVM有条件或无条件从指定指令而不是控制转移指令的下一条指令继续执行程序。控制转移指令包括:

1)条件分支:ifeq,iflt,ifle,ifne,ifgt,ifge,ifnull,ifnotnull,if_cmpeq,if_icmpne,if_icmlt,if_icmpgt等

2)复合条件分支:tableswitch,lookupswitch

3)无条件分支:goto,goto_w,jsr,jsr_w,ret

JVM中有专门的指令集处理int和reference类型的条件分支比较操作,为了可以无明显标示一个实体值是否是null,有专门的指令检测null 值。boolean类型和byte类型,char类型和short类型的条件分支比较操作,都使用int类型的比较指令完成,而 long,float,double条件分支比较操作,由相应类型的比较运算指令,运算指令会返回一个整型值到操作数栈中,随后再执行int类型的条件比较操作完成整个分支跳转。各种类型的比较都最终会转化为int类型的比较操作。

方法调用和返回指令

invokevirtual指令:调用对象的实例方法,根据对象的实际类型进行分派(虚拟机分派)。

invokeinterface指令:调用接口方法,在运行时搜索一个实现这个接口方法的对象,找出合适的方法进行调用。

invokespecial:调用需要特殊处理的实例方法,包括实例初始化方法,私有方法和父类方法

invokestatic:调用类方法(static)

方法返回指令是根据返回值的类型区分的,包括ireturn(返回值是boolean,byte,char,short和 int),lreturn,freturn,drturn和areturn,另外一个return供void方法,实例初始化方法,类和接口的类初始化i 方法使用。

同步

JVM支持方法级同步和方法内部一段指令序列同步,这两种都是通过moniter实现的。

方法级的同步是隐式的,无需通过字节码指令来控制,它实现在方法调用和返回操作中。虚拟机从方法常量池中的方法标结构中的 ACC_SYNCHRONIZED标志区分是否是同步方法。方法调用时,调用指令会检查该标志是否被设置,若设置,执行线程持有moniter,然后执行方法,最后完成方法时释放moniter。

同步一段指令集序列,通常由synchronized块标示,JVM指令集中有monitorenter和monitorexit来支持synchronized语义。

结构化锁定是指方法调用期间每一个monitor退出都与前面monitor进入相匹配的情形。JVM通过以下两条规则来保证结结构化锁成立(T代表一线程,M代表一个monitor):

1)T在方法执行时持有M的次数必须与T在方法完成时释放的M次数相等

2)任何时刻都不会出现T释放M的次数比T持有M的次数多的情况


二、JVM寄存器

JVM只设置了4个最为常用的寄存器。

1、pc程序计数器

2、optop操作数栈顶指针

3、frame当前执行环境指针

4、vars指向当前执行环境中第一个局部变量的指针

所有寄存器均为32位。

pc用于记录程序的执行。optop,frame和vars用于记录指向Java栈区的指针。

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

推荐阅读更多精彩内容