java虚拟机编译_[四] java虚拟机JVM编译器编译代码简介 字节码指令实例 代码到底编译成了什么形式...

前言简介

前文已经对虚拟机进行过了简单的介绍,并且也对class文件结构,以及字节码指令进行了详尽的说明

想要了解JVM的运行机制,以及如何优化你的代码,你还需要了解一下,java编译器到底是如何编译你的代码的

本文不是从最底层的编译原理讲解

本文是针对java代码,去查看归纳总结编译器的结果行为,从而直观的感受到字节码指令集

也就是说本文的内容,主要针对的是使用javap 查看字节码文件中方法的code属性中的字节码内容

让你从java代码  class文件格式,以及字节码指令集 进行一个直观的演示

提醒:

如果你对字节码指令不了解,而且,没有看过前面的文章,本文可能会轻度不适.

本文示例只是为了展示

您应该经常查看你自己的代码的class文件去发现其中的规律

一条普通的指令格式

[ [ ... ]] []

index 表示偏移量 行号  等

opcode 表示操作码

operandX表示操作数

comment 为注释

比如下图所示

行号

0 , 操作码 getstatic ,操作数 #24  注释为 Field

java/lang/System..................

00377b2f3bc6e4976a3f94bb97d0add7.png

其中 index  行号/偏移量  可以作为控制跳转指令的跳转目标  比如 goto  8

表示跳转到索引为8的指令上

还有一点需要注意的是,javap查看到的内容,你可以认为是class文件表述的信息,但是绝不能理解为就是class文件中的内容

比如,class文件中没有操作码的助记符,比如,getstatic

,都是指令的二进制值

再比如刚才说到的,跳转到指定行号,对于控制转移指令,实际的操作数是在当前指令的操作码集合中的地址偏移量

并不是那个8

只不过javap工具按照更适合我们阅读的方式进行了翻译

加载存储与算数指令

public static voidmain(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++;

}

5b29178efa594909416ca7d6def45041.png

-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 =

32767;

15.加载#17号常量池数据到操作数栈

17. 操作数栈保存到6号局部变量表 也就是n = 32768;

b375f76e7aca81724cac6ef00d8ae965.png

19. 加载1号局部变量到操作数栈 对应 i

20.

加载2号局部变量到操作数栈  对应 j

21. 执行iadd指令计算并将结果压入栈顶   对应 i+j;

22.

保存栈顶元素到7号局部变量

24. 加载1号局部变量到操作数栈 对应 i

25. 加载2号局部变量到操作数栈  对应

j

26.执行isub指令计算并将结果压入栈顶   对应i-j;

27. 保存栈顶元素减法结果到8号局部变量

29,30

加载 2号和3号局部变量到操作数栈 也就是j   k

31  执行imul指令并将结果压栈 j*k

32

保存栈顶元素乘法结果到9号局部变量

34.35 加载 2号和3号局部变量到操作数栈 也就是j   k

36 执行idiv

结果压入栈顶

37保存idiv结果到10号局部变量

39.40 加载3号 和 2号 也就是k   j

41 执行求余irem

结果压入栈顶

42 栈顶元素结果保存到11号局部变量

44加载2号局部变量  对应 j 到操作数栈

45

加载常量-1到操作数栈

46 执行异或运算结果压入栈顶  (~x = -1 ^ x;)

47栈顶结果保存到12号局部变量

49

加载1号局部变量 对应 i

50 执行增量 1 计算 结果压入栈顶

53 栈顶结果保存到13号变量

55 void方法

return返回

类型转换指令

public static voidmain(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;

}

javap解析后的内容太长,接下来分段解析

数据的加载与存储

eccad5d4aedacea4ef355669720a63c4.png

从数据的存储可以看得出来

boolean内部使用的是数值1  也就是1 表示true

数据类型转换为char类型

char byte short int  内部形式均为int  所以转换为char是,使用的全都是 i2c

long  float double 先转换为int(l2i f2i d2i) 然后在统一使用

i2c 转换为char

a4e715df79e80b5deeef9b8ac100a660.png

数据类型转换为byte 类型

char byte short int  内部形式均为int  所以转换为byte时,使用的全都是 i2b

long  float double 先转换为int(l2i f2i

d2i) 然后在统一使用 i2b 转换为  byte

40b4b8c2bcd18e5392b9f6a2f0b8e7e8.png

数据类型转换为short 类型

还是同样的道理,char byte short int  内部形式均为int  所以转换为short

使用的是   i2s

long  float double 先转换为int(l2i f2i

d2i) 然后在统一使用 i2s 转换为  short

2ba62cc5da4fe8155691184322745c7e.png

数据类型转换为int 类型

char byte

short内部都是int类型.将他们转换为int时,不需要进行转换

如下图所示,一个load 对应一个store

long  float double    (l2i

f2i d2i)   转换为int

07299be9466aaaf375d38eec81399023.png

数据类型转换为long 类型

char byte short  int   内部都是int类型.将他们转换为long

时,使用  i2l

float double   转换为long   f2l d2l

c252004fa4f53bf2e999b77f596e5476.png

数据类型转换为float 类型

char byte short  int   内部都是int类型.将他们转换为float 时,使用  i2f

long double   转换为float     l2f  d2f

87ba61a922bef6f2e192ecabde563e03.png

数据类型转换为double 类型

char byte short  int   内部都是int类型.将他们转换为double 时,使用  i2d

long

float   转换为double     l2d  f2d

f2464930daa1db871c1bf9871260e24b.png

类相关指令

classSuper{

}class Sub extendsSuper{

}newObject();newSuper();

Super s= newSuper();new Double(1.5);newSub();

Sub sub= new Sub();

8ea5bb8fcb38ac8967ce38c6326a9114.png

new Object();

new Super();

没有赋值给局部变量 仅仅是创建对象  调用new之后,堆中对象的引用保存在栈顶

然后调用构造方法invokespecial

Super s = new Super();

同上面的,需要调用new

因为还需要保存到局部变量

所以new之后 先copy一个,也就是dup

然后调用构造方法 invokespecial

然后从操作数栈保存到局部变量 store

Super super1 = newSuper();

Sub sub= newSub();//父类引用可以指向子类//子类引用不能指向父类//但是对于指向子类的父类引用 可以通过类型转换为子类Super subToSuper=sub;

Sub superToSub= (Sub) subToSuper;

ac20d260e0c8731949e8c4c7177cb8e1.png

0

创建Spper

3 复制

4 调用构造方法

7 保存到1号局部变量

8 创建Sub

11 复制

12调用构造方法

15

保存到2号局部变量

16 2号加载到操作数栈

17保存到3号局部变量

18加载3号局部变量到栈

19 checkcast

进行校验确认是否可以转换为指定类型 否则报错抛 classCastException

22 再次保存到局部变量

控制转移指令

voidintWhile() {int i = 0;while (i < 100) {

i++;

}

}voidintDoWhile() {int i = 0;do{

i++;

}while (i < 100);

}voidintFor() {int j = 0;for(int i =0;i<100;i++) {

j++;

}

}

f2ddb86d9bed7489fee45ac987766e55.png

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 voidfun() {int i = 0;if(i<2) {

i++;

}else{

i--;

}

}

48694e59d394e9141f55ed332feb4e77.png

0, 加载常量0

到栈顶

1,保存栈顶元素 (0) 到1号局部变量

2. 加载1号局部变量到栈顶

3. 加载常量2

到栈顶

4,比较

如果大于后者等于跳转到13 然后1号局部变量 自增1 然后下一步顺序到16 return

否则就是顺序执行到7 1号局部变量

增量为-1  自增运算 然后到10 ,10为跳转到16 return

方法调用相关指令

public voidinvoker() {

method(2);

}public void method(inti) {if(i>5) {

System.out.println(i);

}

}

8b17d6b95b2ec0e396998adb7d50317f.png

invoker()

0,加载0号

局变量到栈   (上面基本都是第一个数据被保存到1号局部变量,0 号其实是被this 占用了)

1,加载常量2

到操作数栈

2.调用实例方法(I)V

5 return

method(int)

0. 加载1号局部变量到操作数栈

1.

加载常量5 到操作数栈

2比较如果小于等于 跳转到12行 直接返回

如果大于

那么顺序执行到5行       out 是类型为PrintStream的   System中的静态变量

8

加载1号局部变量到操作数栈

9 调用实例方法 println  是  PrintStream的实例方法

使用invokevirtual

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;

}

6bb85e3f8d04a3bb6a345dba62fcaef4.png

0,1,2,4

分别将 5 和 6 加载并存储到1号和2号局部变量

5.加载1号局部变量到栈  对应 switch (i)

{

然后根据tableswitch 表 进行跳转

虽然我们只有1,3,5  但是设置了1到5 ,对于2 和 4

直接跳转到default

40: 2号局部变量 +1

顺序到43

43: 跳转到61 return

46: 2号局部变量 +2

顺序到49

49: 跳转到61 return

52: 2号局部变量 +3

顺序到55

55: 跳转到61 return

58 2号局部变量 +4

顺序到61

return

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;

}

ce65a2a22c345481e6bb37a2fa540614.png

0  加载常量6到栈

1 保存到 1 号局部变量

3.加载常量池 #36 到栈

21f85291364fb7fda049faa15290a221.png

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行

路由之后跳转到指定的序列

你会发现三个case他们的过程是一样的

加载3号局部变量 ,然后将常量 A C  hehe

也加载到栈

然后调用equal方法进行比较

7157db4fec9e0d75f86c7274813792bd.png

代码千千万,本文只是找一些基本的示例展示字节码与代码的对应关系,想要熟悉这块

唯有没事多javap看看你代码的class文件,才能通宵领悟,进而更好地优化你的代码

比如看看下面的一个很典型的例子

int i = 5;int j = 8;int k = i+j;int l = 3+6;

6f9f6bee27e3bc5789ba49f1330f1791.png

前一部分:

0.

常量5 加载到栈

1,保存到 1号局部变量

2. 常量8 加载到栈

4 保存到2号 局部变量

5,加载1号局部变量

6,

加载2号局部变量

7 执行iadd 结果会压入栈顶

8 栈顶元素保存到3号局部变量

至此

完成了前三行代码

后一部分:

9.常量9 加载到栈   (3+6  已经被计算好了)

11,保存到4号局部变量

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/360592.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

提高性能:流的非阻塞处理

1.简介 想象一下&#xff0c;我们有一个需要访问外部Web服务的应用程序&#xff0c;以便收集有关客户端的信息&#xff0c;然后对其进行处理。 更具体地说&#xff0c;我们无法在一次调用中获得所有这些信息。 如果我们要查找不同的客户端&#xff0c;则需要多次调用。 如下图…

CDI和EJB:在事务成功时发送异步邮件

再一次问好&#xff01; :) 这次&#xff0c;我选择了一项常见任务&#xff0c;我认为大多数情况下都以错误的方式完成&#xff1a;发送电子邮件。 并非所有人都不知道电子邮件API的工作方式&#xff0c;例如JavaMail或Apache的commons-email 。 我通常看到的一个问题是&#…

使用默认方法的界面演变–第一部分:方法

几周前&#xff0c;我们详细研究了默认方法 -Java 8中引入的一项功能&#xff0c;该功能允许为接口方法提供实现&#xff0c;即方法主体&#xff0c;从而定义接口中的行为。 引入此功能是为了实现接口演进 。 在JDK的上下文中&#xff0c;这意味着在不破坏所有代码的情况下向接…

java两个和三个_Java语言基础(day_03)

数据类型中补充的几个小问题1)在定义Long或者Float类型变量的时候&#xff0c;要加L或者f。整数默认是int类型&#xff0c;浮点数默认晨double。byte&#xff0c;short在定义的肘候&#xff0c;他们接收的某实是一个int类型的值。这个是自己做了一个数据检测的&#xff0c;如果…

在Websphere 8.0上安装Liferay 6.2 Enterprise Edition

为Liferay准备Websphere 当应用服务器二进制文件均已安装完毕&#xff0c;启动WebSphere应用服务器&#xff08;WAS&#xff09; 配置文件管理工具来创建一个配置文件适用于Liferay的和遵循的指示说明这里正式Liferay的文档。 这些说明用于在Websphere 8.5上安装Liferay 6.2&am…

java完数流程图_编程基本功训练:流程图画法及练习

对于“程序设计”的工作&#xff0c;许多初学者的理解就是“写代码”。同样&#xff0c;新手们苦恼的问题是&#xff0c;他们只会“写代码”。当接到一个新的任务&#xff0c;不少人总是在第一时间就爬到键盘上去敲代码。敲着敲着&#xff0c;就把自己绕糊涂了。头晕脑胀地坚持…

java开发环境搭建 pdf_01搭建java web开发环境.pdf

01搭建java web开发环境.pdf还剩19页未读&#xff0c;继续阅读下载文档到电脑&#xff0c;马上远离加班熬夜&#xff01;亲&#xff0c;很抱歉&#xff0c;此页已超出免费预览范围啦&#xff01;如果喜欢就下载吧&#xff0c;价低环保&#xff01;内容要点&#xff1a;( 7)在 M…

在AWS Elastic MapReduce上运行PageRank Hadoop作业

在上一篇文章中&#xff0c;我描述了执行PageRank计算的示例&#xff0c;该示例是使用Apache Hadoop进行Mining Massive Dataset课程的一部分。 在那篇文章中&#xff0c;我接受了Java中现有的Hadoop作业&#xff0c;并做了一些修改&#xff08;添加了单元测试&#xff0c;并通…

java sort 第二个参数_详解java Collections.sort的两种用法

Collections是一个工具类&#xff0c;sort是其中的静态方法&#xff0c;是用来对List类型进行排序的&#xff0c;它有两种参数形式&#xff1a;public static > void sort(List list) {list.sort(null);}public static void sort(List list, Comparator super T> c) {lis…

MVC3 中使用Unity实现依赖注入

前言&#xff1a;前段时间一直在研究依赖注入&#xff0c;不过不是在MVC框架中使用&#xff0c;今天突然想到在MVC中使用Unity实现依赖注入&#xff0c;一时慌了&#xff0c;不知道从何下手&#xff0c;接着就是网上不停的找资料&#xff0c;下面我把我找到的资料分享下&#x…

ms2005 SQL Server设置改为SQL Server身份验证

1.为 SQL Server 2005 Express Edition 或 SQL Server 2005 Developer Edition 启用远程连接 必须为要从远程计算机连接到的每个 SQL Server 2005 实例启用远程连接。为此&#xff0c;请按照下列步骤操作&#xff1a; 1.单击“开始”&#xff0c;依次指向“程序”、“Microsoft…

在Graphite中存储Hystrix的几个月历史指标

Hystrix的杀手级功能之一是低延迟&#xff0c;数据密集型且美观的仪表板 &#xff1a; 即使这只是Hystrix实际操作的副作用&#xff08;断路器&#xff0c;线程池&#xff0c;超时等&#xff09;&#xff0c;它也往往是最令人印象深刻的功能。 为了使其工作&#xff0c;您必须…

NYOJ-----最少乘法次数

最少乘法次数 时间限制&#xff1a;1000 ms | 内存限制&#xff1a;65535 KB难度&#xff1a;3描述给你一个非零整数&#xff0c;让你求这个数的n次方&#xff0c;每次相乘的结果可以在后面使用&#xff0c;求至少需要多少次乘。如24&#xff1a;2*222&#xff08;第一次乘&a…

php投票系统中各个文件的作用说明,PHP开发简单投票系统之投票页面功能模块(二)...

当完成前面的投票后&#xff0c;可以选择点击查看结果查看每个项目的总票数和所有项目的投票百分比。点击“查看结果”后程序会自动计算每个项目的票数和所占百分比。使用了隐藏表单属性隐藏域在页面中对于用户是不可见的&#xff0c;在表单中插入隐藏域的目的在于收集或发送信…

ref 和out 关键字

ref 和out 关键字 通过对CLR的学习&#xff0c;我们可以知道&#xff0c;CLR默认所有方法参数都是传值的。对于引用类型的对象&#xff0c;传递的是对象的引用&#xff08;指向对象的指针&#xff09;&#xff0c;被调用者拥有该对象的引用的拷贝&#xff0c;能够修改对象&…

php-cli下载,php-cli-color

一个简单的 PHP 命令行 cli 输出彩色的类库安装composer require wujunze/php-cli-color ~1.0使用getColoredString("Testing Colors class, this is purple string on yellow background.", "purple", "yellow") . PHP_EOL;echo $colors->ge…

您会后悔对Lambdas应用重载!

编写好的API很难。 非常辛苦。 如果您想让用户喜欢您的API&#xff0c;则必须考虑很多事情。 您必须在以下两者之间找到适当的平衡&#xff1a; 有用性 易用性 向后兼容 前向兼容性 之前&#xff0c;在我们的文章&#xff1a; 如何设计良好的常规API中&#xff0c;我们已经…

如何:优化Hive ZooKeeper Lock Manager实施

Hive一直使用ZooKeeper作为分布式锁定管理器来支持HiveServer2中的并发。 基于ZooKeeper的锁管理器在小型环境中运行良好。 但是&#xff0c;随着越来越多的用户从HiveServer迁移到HiveServer2并开始创建大量并发会话&#xff0c;可能会出现问题。 主要问题是Hiveserver2和ZooK…

《图解HTTP》第1章 了解Web及网络基础

《图解HTTP》第1章 了解Web及网络基础 1. 使用 HTTP 协议访问 Web1.1 网络基础 TCP/IP1.2 TCP/IP 协议族1.2.1 TCP/IP 的分层管理 1. 使用 HTTP 协议访问 Web Web 使用一种名为 HTTP&#xff08;HyperText Transfer Protocol&#xff0c;超文本传输协议&#xff09; 的协议作为…

JBoss Forge NetBeans集成–入门

JBoss Forge是构建基于Maven的Java EE项目的最快方法。 因此&#xff0c;它已经具有了令人敬畏的功能&#xff0c;使您作为开发人员的生活更加轻松。 在大多数情况下&#xff0c;使用Forge的人们可能会对创建Web应用程序感兴趣。 有很多入门Forge基础的方法。 您可以查看quick…