前言

之前我们都只是编写运行java程序,很少了解java程序从编译到执行到底经历了什么。从编译到执行大致可以分为四个阶段:编译->加载->解释->执行,本文介绍了这几个阶段主要发生的事情以及作用。


一、跨平台

Java程序是由JAVA虚拟机负责解释执行的,而非操作系统.实现java程序的跨平台运行,运行相同的java程序只需要安装不同版本的java虚拟机即可.

在这里插入图片描述

不同的操作系统使用不同版本的java虚拟机.这种方式使得java语言具有一次编译,到处运行的特性


二、编译

编译:将源码文件(.java)编译成JVM可以解释的class文件。

编译过程会对源代码程序(.java)做 「语法分析」「语义分析」「注解处理」等等处理,
最后才生成字节码文件(.class)。

注意:对泛型的擦除就是在编译阶段发生的。

三、加载

字节码存储在硬盘上,需要运行时,由类加载系统负责将类的信息加载到内存中(方法区),使用的是类加载器进行加载,充当一个快递员角色。
在这里插入图片描述

1.装载:查找并加载类的二进制数据,在JVM堆中创建一个java.lang.Class类的对象,并将类相关的信息存储在JVM方法区中。

装载时机:
为了节省内存的开销,并不会一次性把所有的类都装载至JVM
有需要的时候才进行装载(比如new和反射等等)

装载发生:
class文件是通过类加载器,装载到jvm中的
类加载时有一个很重要的原则:双亲委派机制(在后续的文章中会介绍到)

装载规则:
JDK中的核心类一般由启动类加载器(Bootstrploader)装载
JDK中内部实现的扩展类一般由扩展加载器(ExtClassLoader )实现装载
自己写的程序中的类则由应用程序加载器(AppClassLoader )实现装载

2.连接:可以分为三个步骤:验证->准备->解析

验证:验证类是否符合 Java 规范和 JVM 规范
1.验证字节码格式,是否被修改(污染)
2.验证语法,例如类是否继承final的类......
准备:为类的静态变量分配内存,初始化为系统的初始值。
注意: 在准备阶段不为静态的常量进行赋值(static final 在编译时直接赋值)
解析:将符号引用(文件中的逻辑引用)转为直接引用(内存中的地址)

3.初始化:给类的静态变量赋予正确的初始值。

例如:static int num=123;
在准备阶段 static int num=0;      始化阶段static int num=123;
总结:收集class的静态变量、静态代码块、静态方法至()方法,随后从上往下开始执行。

注意:这里的初始化是指类的初始化,只和静态的相关,与对象的初始化不同!

类什么时候被加载?
1. 使用new关键字创建对象。
2. 使用类的静态属性(非常量)和静态方法。
3. 使用反射加载类 Class.forName()。
4. 子类被加载(因为子类在加载时会调用父类的构造方法,父类也当被加载)。

注意:只访问类中的静态常量(static final int num=1)不会触发类加载。


四、解释

解释:由执行引擎把字节码解释为操作系统识别的指令。
在这里插入图片描述
两种方式把字节码信息解释成机器指令码:
1.字节码解释器(直接解释)
特点:jvm运行时程序时,逐行对字节码指令进行翻译, 效率低。

2.即时编译器(JIT)(编译解释)
特点:对某段代码整体编译后执行 效率高 编译需要耗费一定的时间。

JVM会对热点代码做编译,非热点代码直接进行解释。
当JVM发现某个方法或代码块的运行特别频繁的时候,就有可能把这部分代码认定为热点代码。

HotSpot使用的是计数器的方式进行探测,为每个方法准备了两类计数器:方法调用计数器和回边计数器
这两个计数器都有一个确定的阈值,当计数器超过阈值溢出了,就会触发JIT编译。

注意:这里的编译是值class文件编译为指令,与之前.java编译成.class的编译不同
Hello.java-----jdk   编译工具 javac  ----.class  称为前端编译.
Hello.class-----执行引擎  编译为 指令   称为后端编译      

例如:如果我们要看一本英文书籍,我们可以读一句翻译一句(这就是直接解释),也可以先将整个书籍先全部翻译在阅读(这就是编译解释)。显然如果我们需要对一段文字重复(热点代码)阅读的话,编译解释的效率就会高很多。


五、执行

总结:操作系统识别解释阶段生成的指令,调用系统的硬件执行最终的程序指令。


总结

Java因为有JVM从而具有一次编译,到处运行的特点。
Java源码到执行的过程,从JVM的角度看可以总结为四个步骤:编译->加载->解释->执行。

1.编译:
语法分析->语义分析->注解处理   生成class文件。

2.加载:
     装载:则把class文件装载至JVM。
     连接:校验class信息、分配内存空间及赋默认值。
     初始化:为变量赋值为正确的初始值。
     
3.解释:字节码转换成操作系统可识别的执行指令。

4.执行:操作系统识别解释阶段生成的指令,调用系统的硬件执行最终的程序指令。


版权声明:本文为qq_52173163原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。
原文链接:https://blog.csdn.net/qq_52173163/article/details/125022382