以下内容来自:

http://www.jianshu.com/p/ac7760655d9d

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栈区的指针。

作者:空有一身才华和一张帅气的脸
链接:http://www.jianshu.com/p/ac7760655d9d
來源:简书
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。

JVM深入理解<二>的更多相关文章

  1. java之jvm学习笔记二(类装载器的体系结构)

    java的class只在需要的时候才内转载入内存,并由java虚拟机的执行引擎来执行,而执行引擎从总的来说主要的执行方式分为四种, 第一种,一次性解释代码,也就是当字节码转载到内存后,每次需要都会重新 ...

  2. JVM深入理解

    JVM深入理解 一.JVM介绍 JVM应用百度百科的原话是: JVM是Java Virtual Machine(Java虚拟机)的缩写,JVM是一种用于计算设备的规范,它是一个虚构出来的计算机,是通过 ...

  3. JVM的理解

    1.Java程序是交由JVM执行的,所以我们在谈Java内存区域划分的时候事实上是指JVM内存区域划分.在讨论JVM内存区域划分之前,先来看一下Java程序具体执行的过程: 也相当与 注:JVM(ja ...

  4. JVM如何理解Java泛型类(转)

    一个很典型的泛型(generic)代码.T是类型变量,可以是任何引用类型: public class Pair<T>{ private T first=null; private T se ...

  5. JVM如何理解Java泛型类

    //泛型代码 public class Pair<T>{ private T first=null; private T second=null; public Pair(T fir,T  ...

  6. 谈谈对JVM的理解

            JVM可谓是学习JAVA基础中的基础了,但仍有不少同学对JVM概念还是比较模糊,甚至没有听说过,对java的理解也只是在基础语法 层面,本文就将对JVM进行初步介绍,因篇幅所限,只能介 ...

  7. JVM 系列(二)内存模型

    02 JVM 系列(二)内存模型 一.JVM 内存区域 JVM 会将 Java 进程所管理的内存划分为若干不同的数据区域.这些区域有各自的用途.创建/销毁时间: 一. 线程私有区域 线程私有数据区域生 ...

  8. [转帖]JVM—深入理解内存模型与垃圾收集机制

    JVM—深入理解内存模型与垃圾收集机制 https://juejin.im/post/5d68dc9ee51d4561ad6548f7 前言 Java是一种跨平台的语言,当初其设计初衷也是为了解决各个 ...

  9. Java 中级 学习笔记 1 JVM的理解以及新生代GC处理流程

    写在最前 从毕业到现在已经过去了差不多一年的时间,工作还算顺利,但总是离不开CRUD ,我觉得这样下去肯定是不行的,温水煮青蛙,势必有一天,会昏昏沉沉的迷失在温水里.所以,需要将之前学习JAVA 当中 ...

随机推荐

  1. Maven项目运行Junit测试用例 出现ClassNotFound

    Maven 打包命令 clean package 会清空target下的目录 包含 test-classes 目录 这样执行junit的时候,会出现 ClassNotFound的错误 执行下面的命令即 ...

  2. 在GridControl控件上绑定图片的几种操作方式

    我们知道,基于DevExpress的开发Winform的项目界面的时候,GridControl控件是经常用来绑定数据的,一般以常规的字符内容为主,有时候也会有图片的显示需要,那么如果显示图片,我们应该 ...

  3. mybatis抽取出的工具-(一)通用标记解析器(即拿即用)

    目录 1. 简介 1.1 mybatis-config.xml 中使用 1.2 xxxMapper.xml 中使用 2. 原理 2.1 GenericTokenParser 成员变量 2.2 Gene ...

  4. 通过 Systemd Journal 收集日志

    随着 systemd 成了主流的 init 系统,systemd 的功能也在不断的增加,比如对系统日志的管理.Systemd 设计的日志系统好处多多,这里笔者就不再赘述了,本文笔者主要介绍 syste ...

  5. 03 Django REST Framework 视图和路由

    01-DRF中的request 在Django REST Framework中内置的Request类扩展了Django中的Request类,实现了很多方便的功能--如请求数据解析和认证等. 比如,区别 ...

  6. iStack堆叠介绍

    iStack堆叠技术简介:   网络中主要存在两种形态的通信设备:盒式设备和框式设备.通常盒式设备部署在网络接入层或对可靠性要求不高的汇聚层,盒式单机设备对端口和带宽扩容不够灵活,扩容增加新的盒式设备 ...

  7. Python-待

    内置函数总结 https://www.cnblogs.com/jason-lv/p/8243141.html https://www.cnblogs.com/pyyu/p/6702896.html 数 ...

  8. 小L的项链切割 (回文串)

    题目描述 小T送给了小L了一串项链.为了方便,我们把项链上形态不同钻石用不同的字母表示.这样小L的项链就变成了一个字符串.小L忽然想把这串项链优美地切割一下,她想把它切割成尽量少的回文项链,啊也就是回 ...

  9. 聊一聊跨域,Vue向Django请求数据的一些问题

    1.做前后端分离 前端使用Vue程序,后端使用Django配合rest-framework. 那么前端Vue通过API接口拿到数据会出现跨域的问题,JSONP只是在get中会用到的,所以这里使用cor ...

  10. jmeter分布式压测(多台电脑一起压测)

    (1)在Windows下运行 操作步骤: 1)     有多台电脑,每台电脑上都有jmeter,而且这几台电脑都互相能ping通. 2)     在我的电脑的jmeter的配置文件bin目录下的jme ...