[四] java虚拟机JVM编译器编译代码简介 字节码指令实例 代码到底编译成了什么形式
前言简介
<index> <opcode> [ <operand1> [ <operand2>... ]] [<comment>]
index 表示偏移量 行号 等
opcode 表示操作码
operandX表示操作数
comment 为注释
|
0 , 操作码 getstatic ,操作数 #24 注释为 Field
java/lang/System..................
其中 index 行号/偏移量 可以作为控制跳转指令的跳转目标 比如 goto 8 表示跳转到索引为8的指令上 |
,都是指令的二进制值
加载存储与算数指令
public static void main(String[] args) { int i = -1;
int j = 3;
int k = 5;
int l = 127;
int m = 32767;
int n = 32768; int add = i+j;
int sub = i-j;
int mul = j*k;
int div = j/k;
int rem = k%j;
int neg = ~j;
int inc = i++; }
-1 ~ 5 使用const加载到操作数栈 其中-1 使用iconst_m1 -128~127 使用bipush -32768~32767使用sipush 其余常量池ldc store从操作数栈保存到局部变量表 load加载局部变量到操作数栈 |
0. 常量-1 加载到操作数栈 1. 操作数栈保存到1号局部变量表 也就是 i = -1; 2. 常量 3 加载到操作数栈 3. 操作数栈保存到2号局部变量表 也就是j = 3; 4. 常量 5 加载到操作数栈 5. 操作数栈保存到3号局部变量表 也就是k =5; 6. 常量 127 加载到操作数栈 8. 操作数栈保存到4号局部变量表 也就是l = 127;
10.常量 32767 加载到操作数栈
13.操作数栈保存到5号局部变量表 也就是m = 19. 加载1号局部变量到操作数栈 对应 i |
类型转换指令
public static void main(String[] args) {
boolean bNum = true; char cNum = 2;
byte byteNum = 127;
short sNum = 32767;
int iNum = 100;
long lNum = 65536;
float fNum = 2.5f;
double dNum = 6.8;
char c1 = (char)byteNum;
char c2 = (char)sNum;
char c3 = (char)iNum;
char c4 = (char)lNum;
char c5 = (char)fNum;
char c6 = (char)dNum; byte b1 = (byte)cNum;
byte b2 = (byte)sNum;
byte b3 = (byte)iNum;
byte b4 = (byte)lNum;
byte b5 = (byte)fNum;
byte b6 = (byte)dNum; short s1 = (short)cNum;
short s2 = (short)byteNum;
short s3 = (short)iNum;
short s4 = (short)lNum;
short s5 = (short)fNum;
short s6 = (short)dNum; int i1 = (int)cNum;
int i2 = (int)byteNum;
int i3 = (int)sNum;
int i4 = (int)lNum;
int i5 = (int)fNum;
int i6 = (int)dNum; long l1 = (long)byteNum;
long l2 = (long)cNum;
long l3 = (long)sNum;
long l4 = (long)iNum;
long l5 = (long)fNum;
long l6 = (long)dNum; float f1 = (float)byteNum;
float f2 = (float)cNum;
float f3 = (float)sNum;
float f4 = (float)iNum;
float f5 = (float)lNum;
float f6 = (float)dNum; double d1 = (double)byteNum;
double d2 = (double)cNum;
double d3 = (double)sNum;
double d4 = (double)iNum;
double d5 = (double)lNum;
double d6 = (double)fNum;
}
数据的加载与存储
boolean内部使用的是数值1 也就是1 表示true
数据类型转换为char类型
i2c 转换为char
数据类型转换为byte 类型
d2i) 然后在统一使用 i2b 转换为 byte
数据类型转换为short 类型
使用的是 i2s
d2i) 然后在统一使用 i2s 转换为 short
数据类型转换为int 类型
short内部都是int类型.将他们转换为int时,不需要进行转换
f2i d2i) 转换为int
数据类型转换为long 类型
时,使用 i2l
数据类型转换为float 类型
数据类型转换为double 类型
类相关指令
class Super{
} class Sub extends Super{
} new Object();
new Super();
Super s = new Super();
new Double(1.5);
new Sub();
Sub sub = new Sub();
new Object();
new Super();
没有赋值给局部变量 仅仅是创建对象 调用new之后,堆中对象的引用保存在栈顶
然后调用构造方法invokespecial
Super s = new Super();
同上面的,需要调用new
因为还需要保存到局部变量
所以new之后 先copy一个,也就是dup
然后调用构造方法 invokespecial
然后从操作数栈保存到局部变量 store
|
Super super1 = new Super();
Sub sub = new Sub(); //父类引用可以指向子类
//子类引用不能指向父类
//但是对于指向子类的父类引用 可以通过类型转换为子类
Super subToSuper = sub;
Sub superToSub = (Sub) subToSuper;
0 创建Spper 3 复制 4 调用构造方法 7 保存到1号局部变量 8 创建Sub 11 复制 12调用构造方法 15 保存到2号局部变量 16 2号加载到操作数栈 17保存到3号局部变量 18加载3号局部变量到栈 19 checkcast 进行校验确认是否可以转换为指定类型 否则报错抛 classCastException 22 再次保存到局部变量 |
控制转移指令
void intWhile() {
int i = 0;
while (i < 100) {
i++;
}
} void intDoWhile() {
int i = 0;
do {
i++;
}
while (i < 100);
} void intFor() {
int j = 0;
for(int i =0;i<100;i++) {
j++;
}
}
intWhile()方法 0. 加载常量0 到操作数栈 1.保存操作数栈元素到1号局部变量 i= 0; 2.直接跳转到第8行 8.1号局部变量加载到操作数栈 也就是i 作为第一个元素 9.加载常量100到操作数栈 也就是100作为第二个元素 11.比较大小,如果前者小于后者 也就是如果 i <100 满足 跳转到第5行 否则顺序执行到14 return 5.给1号局部变量以增量1 增加 然后 8-->9-->11-->5-->8-->9-->11......往复循环 直到条件不满足,从11 跳转到14 结束 |
intDoWhile() 0.加载常量0到操作数栈 1.保存常量0 到1号局部变量 2.给1号局部变量以增量1 进行自增 5.1号局部变量加载到操作数栈 6.常量100加载到操作数栈 8,比较大小 如果前者小于后者也就是 1号局部变量 i<100 跳转到第2行 然后进行往复循环,直到条件不满足,然后顺序到return |
intFor() 0. 加载常量0 到操作数栈 1. 保存栈顶元素到1号局部变量 j=0; 2. 加载常量0到操作数栈 3. 保存栈顶元素到2号局部变量i=0; 4. 跳转到13行 13. 加载2号局部变量到操作数栈 14. 加载常量100到操作数栈 16. 比较大小,如果前者 2号局部变量 i <100 跳转到7 7. 1号局部变量以增量1 自增 j++ 10. 2号局部变量以增量1 自增 i++ 13. 2号局部变量加载到操作数栈 14. 加载常量100到操作数栈
16. 比较大小,如果前者 2号局部变量 i <100 跳转到7
往复循环 如果条件不满足 从16 顺序到19 结束方法 return
|
public void fun() {
int i = 0;
if(i<2) {
i++;
}else {
i--;
}
}
0, 加载常量0 到栈顶 1,保存栈顶元素 (0) 到1号局部变量 2. 加载1号局部变量到栈顶 3. 加载常量2 到栈顶 4,比较 如果大于后者等于跳转到13 然后1号局部变量 自增1 然后下一步顺序到16 return 否则就是顺序执行到7 1号局部变量 增量为-1 自增运算 然后到10 ,10为跳转到16 return |
方法调用相关指令
public void invoker() {
method(2);
} public void method(int i) {
if(i>5) {
System.out.println(i);
}
}
invoker() 0,加载0号 局变量到栈 (上面基本都是第一个数据被保存到1号局部变量,0 号其实是被this 占用了) 1,加载常量2 到操作数栈 2.调用实例方法(I)V 5 return method(int) |
switch 相关
int i = 5;
int j = 6;
switch (i) {
case 1:
j = j + 1;
break;
case 3:
j = j + 2;
break;
case 5:
j = j + 3;
break;
default:
j = j + 4;
}
0,1,2,4 分别将 5 和 6 加载并存储到1号和2号局部变量 5.加载1号局部变量到栈 对应 switch (i) { 然后根据tableswitch 表 进行跳转 虽然我们只有1,3,5 但是设置了1到5 ,对于2 和 4 直接跳转到default 40: 2号局部变量 +1 46: 2号局部变量 +2
顺序到49
49: 跳转到61 return
52: 2号局部变量 +3
顺序到55
55: 跳转到61 return
58 2号局部变量 +4 |
int j = 6;
String string = "hehe";
switch (string) {
case "A":
j = j + 1;
break;
case "hehe":
j = j + 2;
break;
case "C":
j = j + 3;
break;
default:
j = j + 4;
}
0 加载常量6到栈 1 保存到 1 号局部变量 3.加载常量池 #36 到栈 5 保存到2 号局部变量 6 加载2号局部变量 到栈 7 复制栈顶元素 8 复制的元素保存到3号局部变量 9 调用String实例化方法hashCode 12, lookupswitch表中,不在类似tableswitch 了,那个是连续的 lookupswitch 是不连续的 我们总共有三个case一个default lookupswitch 总共有4项 "A" 的hashCode 为 65 "C" 的hashCode为 67 "hehe" 的hashCode为 3198650 不信的话,自己写个main方法打印下 经过12行 |
int i = 5;
int j = 8;
int k = i+j; int l = 3+6;
前一部分: 0. 常量5 加载到栈 1,保存到 1号局部变量 2. 常量8 加载到栈 4 保存到2号 局部变量 5,加载1号局部变量 6, 加载2号局部变量 7 执行iadd 结果会压入栈顶 8 栈顶元素保存到3号局部变量 至此 完成了前三行代码 后一部分: |
[四] java虚拟机JVM编译器编译代码简介 字节码指令实例 代码到底编译成了什么形式的更多相关文章
- 深入理解Java虚拟机(类文件结构+类加载机制+字节码执行引擎)
目录 1.类文件结构 1.1 Class类文件结构 1.2 魔数与Class文件的版本 1.3 常量池 1.4 访问标志 1.5 类索引.父索引与接口索引集合 1.6 字段表集合 1.7 方法集合 1 ...
- 深入理解Java虚拟机读书笔记5----虚拟机字节码执行引擎
五 虚拟机字节码执行引擎 1 运行时栈帧结构 ---栈帧是用于支持虚拟机进行方法调用和方法执行的数据结构,是虚拟机运行时数据区中的虚拟机栈的栈元素. ---栈帧中存储了方法的局部变 ...
- JVM学习第三天(JVM的执行子系统)之字节码指令
早上看了Class类文件结构,晚上继续来看字节码指令,毕竟谁也不是一步登天的(说白了还是穷); 字节码指令 Java虚拟机的指令由一个字节长度的.代表着某种特定操作含义的数字(称为操作码,Opcode ...
- Java虚拟机JVM学习03 连接过程:验证、准备、解析
Java虚拟机JVM学习03 连接过程:验证.准备.解析 类被加载后,就进入连接阶段. 连接就是将已经读入到内存的类的二进制数据合并到虚拟机的运行时环境中去. 连接阶段三个步骤:验证.准备和解析. 类 ...
- 深入理解java虚拟机JVM(下)
深入理解java虚拟机JVM(下) 链接:https://pan.baidu.com/s/1c6pZjLeMQqc9t-OXvUM66w 提取码:uwak 复制这段内容后打开百度网盘手机App,操作更 ...
- 【JVM源码解析】模板解释器解释执行Java字节码指令(上)
本文由HeapDump性能社区首席讲师鸠摩(马智)授权整理发布 第17章-x86-64寄存器 不同的CPU都能够解释的机器语言的体系称为指令集架构(ISA,Instruction Set Archit ...
- 【synchronized锁】通过synchronized锁 反编译查看字节码指令分析synchronized关键字修饰方法与代码块的区别
前提: 首先要铺垫几个前置的知识: Java中的锁如sychronize锁是对象锁,Java对象头中具有标识位,当对象锁升级为重量级锁时,重量级锁的标识位会指向监视器monitor, 而每个Java对 ...
- 从字节码指令看重写在JVM中的实现
Java是解释执行的.包含动态链接的特性.都给解析或执行期间提供了非常多灵活扩展的空间.面向对象语言的继承.封装和多态的特性,在JVM中是怎样进行编译.解析,以及通过字节码指令怎样确定方法调用的版本号 ...
- Java虚拟机JVM内存分区及代码执行机制
详见:http://blog.yemou.net/article/query/info/tytfjhfascvhzxcyt230 1. JVM体系结构 图1 JVM体系结构 方法区:存放JVM ...
随机推荐
- [JZOJ3615]【NOI2014模拟】数列(平面几何+二维线段树)
Description 给定一个长度为n的正整数数列a[i]. 定义2个位置的f值为两者位置差与数值差的和,即f(x,y)=|x-y|+|a[x]-a[y]|. 你需要写一个程序支持2种操作(k都是正 ...
- yii2 注册一个新事件(trigger Event)
有些时候我们需要在某个方法的中间注册一个新事件,确保某些业务的可拓展性. 下面我介绍一下注册一个新事件的方法: 第一步:需要的地方(比如控制器或模型)中定义一个事件常量(如:const EVENT_C ...
- Summer Project
Summer Project Summer是一个用于学习交流,基于Netty4.x的简单mvc库. 使用 快速开始 public class Application { public static v ...
- node04
1.模板引擎 用于渲染页面 介绍jade或ejs jade:侵入式,与原生html/css不共存,使用缩进代表层级 模板后缀.jade ejs:则非侵入式的 2.jade 1)简单使用: //代码 c ...
- hbase数据原理及基本架构
第一:hbase介绍 hbase是一个构建在hdfs上的分布式列存储系统: hbase是apache hadoop生态系统中的重要一员,主要用于海量结构化数据存储 从逻辑上讲,hbase将数据按照表. ...
- Git生成ssh密钥指定文件
ssh-keygen 使用的时候可以直接使用 -f 参数 指定密钥保存文件,省去后面生成成功后再提示选择保存文件: ssh-keygen -t rsa -C "abc@example.com ...
- 俄罗斯方块(三):"流动"的方块
问题的提出: 俄罗斯方块允许90度的坡,是不是有点不够科学#(滑稽) 想办法加一种会“滑坡”的方块 本文两大部分: 详细的描绘是怎样的“流动” 写代码,并整合进游戏 本文基于我写的 俄罗斯方块(一): ...
- 贪心算法----区间选点问题(POJ1201)
题目: 题目的大致意思是,给定n个闭区间,并且这个闭区间上的点都是整数,现在要求你使用最少的点来覆盖这些区间并且每个区间的覆盖的点的数量满足输入的要求点覆盖区间的数量. 输入: 第一行输入n,代表n个 ...
- [Swift]LeetCode914.一副牌中的X | X of a Kind in a Deck of Cards
In a deck of cards, each card has an integer written on it. Return true if and only if you can choos ...
- [Swift]LeetCode922.按奇偶排序数组 II | Sort Array By Parity II
Given an array A of non-negative integers, half of the integers in A are odd, and half of the intege ...