java代码是怎么运行的?

为什么Java要在虚拟机里运行?

java程序抽象程度高,肯定不能直接在硬件上运行。运行之前肯定需要先转换程机器码。不同的机器转换出的机器码肯定不一样。

1.使用虚拟机Java转换出的字节码是一致的,通过java虚拟机字节码转换为对应的机器码,因此java才有了跨平台的效果,由于字节码不变,java程序就可以在不同平台上的虚拟机运行。

2.提供托管环境,自动内存管理与垃圾回收。托管环境还提供了诸如数组越界、动态类型、安全权限等等的动态监测。

 

Java虚拟机具体是怎样运行Java字节码的?

以标准JDK中的HotSpot虚拟机为例,从虚拟机以及低层硬件两个角度,分析Java虚拟机具体是怎么运行Java字节码的。

从虚拟机视角来看,执行Java代码首先需要将它编译成的class文件加载到Java虚拟机中。加载后的Java类会存放于方法区(Method Area)中。实际运行时,虚拟机会执行方法区内的代码。

不同的是,Java虚拟机会将栈细分为面向Java方法的Java方法栈,面向本地方法(用C++写的native方法)的本地方法栈,以及存放各个线程执行位置的PC寄存器。

 

在运行过程中,每当调用进入一个Java方法,Java虚拟机会在当前线程的Java方法栈中生成一个栈帧,用于存放局部变量以及字节码的操作数。这个栈帧的大小是提前计算好的,而且Java虚拟机不要求栈帧在内存空间里连续分布。

当退出当前执行的方法时,不管是正常返回还是异常返回,Java虚拟机均会弹出当前线程的当前栈帧,并将之舍弃。

从硬件视角来看,Java字节码无法直接执行。因此,Java虚拟机需要将字节码翻译成机器码。

在HotSpot里面,上述翻译过程有两种形式:第一种是解释执行,即逐条将字节码翻译成机器码并执行;第二种是即时编译(Just-In-Time compilation,JIT),即将一个方法中包含的所有字节码编译成机器码后再执行。

前者的优势在于无需等待编译,而后者的优势在于实际运行速度更快。HotSpot默认采用混合模式,综合了解释执行和即时编译两者的优点。它会先解释执行字节码,而后将其中反复执行的热点代码,以方法为单位进行即时编译。

 

Java虚拟机的运行效率究竟是怎么样的?

HotSpot采用了多种技术来提升启动性能以及峰值性能,即时编译便是其中最重要的技术之一。

即时编译建立在程序符合二八定律的假设上,也就是百分之二十的代码占据了百分之八十的计算资源。

对于占据大部分的不常用的代码,我们无需耗费时间将其编译成机器码,而是采取解释执行的方式运行;另一方面,对于仅占小部分的热点代码,我们则可以将其编译成机器码,以达到理想的运行速度。

理论上讲,即时编译后的Java程序的执行效率,是可能超过C++程序的。

为了满足不同用户场景的需要,HotSpot内置了多个即时编译器:C1、C2和Graal。Graal是Java10正式引入的实验性即时编译器。

之所以引入多个即时编译器,是为了在编译时间和生成代码的执行效率之间进行取舍。C1又叫做Client编译器,面向的是对启动性能有要求的客户端GUI程序,采用的优化手段相对简单,因此编译时间较短。

C2又叫做Server编译器,面向的是对峰值性能有要求的服务器端程序,采用的优化手段相对复杂,因此编译时间较长,但同时生成代码的执行效率较高。

从Java7开始,HotSpot默认采用分层编译的方式:热点方法首先会被C1编译,而后热点方法中的热点会进一步被C2编译。

为了不干扰应用的正常运行,HotSpot的即时编译是放在额外的编译线程中进行的。HotSpot会根据CPU的数量设置编译线程的数目,并且按1:2的比例配置给C1及C2编译器。

在计算资源充足的情况下,字节码的解释执行和即时编译可同时进行。编译完成后的机器码会在下次调用该方法时启用,以替换原本的解释执行。

总结与实践

https://ci.adoptopenjdk.net/view/Dependencies/job/asmtools/ asmtools.jar下载链接

echo '
public class Foo {
 public static void main(String[] args) {
  boolean flag = true;
  if (flag) System.out.println("Hello, Java!");
  if (flag == true) System.out.println("Hello, JVM!");
 }
}' > Foo.java
$ javac Foo.java
$ java Foo
$ java -cp /path/to/asmtools.jar org.openjdk.asmtools.jdis.Main Foo.class > Foo.jasm.1
$ awk 'NR==1,/iconst_1/{sub(/iconst_1/, "iconst_2")} 1' Foo.jasm.1 > Foo.jasm
$ java -cp /path/to/asmtools.jar org.openjdk.asmtools.jasm.Main Foo.jasm
$ java Foo

https://blog.csdn.net/jiaobuchong/article/details/83037467 awk命令参考

 

执行过程会发现jvm把boolean当做int来处理

flag = iconst_1 = true;

awk把stackframe中的flag改为iconst_2

if(flag)比较时ifeq指令做是否为零判断,常数2仍为true,打印输出

if(true==flag)比较时if_icmpne做整数比较,iconst_1是否等于flag,比较失败,不再打印输出

 

https://www.cnblogs.com/longjee/p/8675771.html 字节码命令参考:ifeq,if_icmpne

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值