JVM深入理解<二>
以下内容来自:
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深入理解<二>的更多相关文章
- java之jvm学习笔记二(类装载器的体系结构)
java的class只在需要的时候才内转载入内存,并由java虚拟机的执行引擎来执行,而执行引擎从总的来说主要的执行方式分为四种, 第一种,一次性解释代码,也就是当字节码转载到内存后,每次需要都会重新 ...
- JVM深入理解
JVM深入理解 一.JVM介绍 JVM应用百度百科的原话是: JVM是Java Virtual Machine(Java虚拟机)的缩写,JVM是一种用于计算设备的规范,它是一个虚构出来的计算机,是通过 ...
- JVM的理解
1.Java程序是交由JVM执行的,所以我们在谈Java内存区域划分的时候事实上是指JVM内存区域划分.在讨论JVM内存区域划分之前,先来看一下Java程序具体执行的过程: 也相当与 注:JVM(ja ...
- JVM如何理解Java泛型类(转)
一个很典型的泛型(generic)代码.T是类型变量,可以是任何引用类型: public class Pair<T>{ private T first=null; private T se ...
- JVM如何理解Java泛型类
//泛型代码 public class Pair<T>{ private T first=null; private T second=null; public Pair(T fir,T ...
- 谈谈对JVM的理解
JVM可谓是学习JAVA基础中的基础了,但仍有不少同学对JVM概念还是比较模糊,甚至没有听说过,对java的理解也只是在基础语法 层面,本文就将对JVM进行初步介绍,因篇幅所限,只能介 ...
- JVM 系列(二)内存模型
02 JVM 系列(二)内存模型 一.JVM 内存区域 JVM 会将 Java 进程所管理的内存划分为若干不同的数据区域.这些区域有各自的用途.创建/销毁时间: 一. 线程私有区域 线程私有数据区域生 ...
- [转帖]JVM—深入理解内存模型与垃圾收集机制
JVM—深入理解内存模型与垃圾收集机制 https://juejin.im/post/5d68dc9ee51d4561ad6548f7 前言 Java是一种跨平台的语言,当初其设计初衷也是为了解决各个 ...
- Java 中级 学习笔记 1 JVM的理解以及新生代GC处理流程
写在最前 从毕业到现在已经过去了差不多一年的时间,工作还算顺利,但总是离不开CRUD ,我觉得这样下去肯定是不行的,温水煮青蛙,势必有一天,会昏昏沉沉的迷失在温水里.所以,需要将之前学习JAVA 当中 ...
随机推荐
- Linux 用户身份与进程权限
在学习 Linux 系统权限相关的主题时,我们首先关注的基本都是文件的 ugo 权限.ugo 权限信息是文件的属性,它指明了用户与文件之间的关系.但是真正操作文件的却是进程,也就是说用户所拥有的文件访 ...
- flask 更新数据库
在做项目的过程中,我们都遇到过,经常需要修改我们数据库的字段,在flask中,是通过ORM(对象关系映射)来创建数据库的,表--->model class,字段---->属性 在flask ...
- itoa()函数和atoi()函数详解
C语言提供了几个标准库函数,可以将任意类型(整型.长整型.浮点型等)的数字转换为字符串. 以下是用itoa()函数将整数转换为字符串的一个例子:# include <stdio.h># i ...
- Python的socket模块与交互式指令
socket简介 在编程的过程中,我们需要使用网络编程,这时我们不得不和网络通信的底层基础打交道了.我们必须让自己传输的数据符合网络通信的基本协议,即TCP/IP协议,但是网络通信协议本身很复杂.我们 ...
- VO和DO转换(四) MapStruct
VO和DO转换(一) 工具汇总 VO和DO转换(二) BeanUtils VO和DO转换(三) Dozer VO和DO转换(四) MapStruct MapStruct
- #Leetcode# 989. Add to Array-Form of Integer
https://leetcode.com/problems/add-to-array-form-of-integer/ For a non-negative integer X, the array- ...
- 开发神器之phpstorm破解与日常使用
PhpStorm 是 JetBrains 公司开发的一款商业的 PHP 集成开发工具,旨在提高用户效率,可深刻理解用户的编码,提供智能代码补全,快速导航以及即时错误检查. PhpStorm可随时帮助用 ...
- 使用PL/SQL连接Oracle时报错ORA-12541: TNS: 无监听程序
因公司需求,安装oracle数据库,oracle数据库用账号密码可以登录,然后在pl/sql里面不能登录,显示无监听程序. 这就说明可能有些服务没有启动,开始运行services.msc ,进入后寻找 ...
- [新三板摘牌]国资企业济南华光光电去年终止拟IPO今年摘牌新三板
国资企业济南华光光电去年终止拟IPO今年摘牌新三板 http://blog.sina.com.cn/s/blog_e32cfa770102ycku.html http://stock.qlmoney. ...
- git fetch 更新远程代码到本地仓库
理解 fetch 的关键, 是理解 FETCH_HEAD,FETCH_HEAD指的是: 某个branch在服务器上的最新状态’.这个列表保存在 .Git/FETCH_HEAD 文件中, 其中每一行对应 ...