Java编译期和运行期&JVM转载

原创
小哥 3年前 (2022-10-27) 阅读数 6 #大杂烩

Java编译代码编译Java源代码编译器按如下方式完成流程图:

Java执行字节码的执行JVM执行引擎完成,流程图如下:

如下图,Java从源文件的创建到程序的运行有两个步骤:1源文件由编译器编译成字节码(ByteCode)  2,字节码java运行虚拟机解释。因为java这个程序既要经过编译,又要通过。JVM可以说,这个解释是有道理的。Java称为半解释性语言( "semi-interpreted" language)。

图1   java程序编译和运行过程

下面通过这个java过程,以说明java程序从编译到最终运行的整个过程。代码如下:

Java代码

  1. //MainApp.java
  2. public class MainApp {
  3. public static void main(String[] args) {
  4. Animal animal = new Animal("Puppy");
  5. animal.printName();
  6. }
  7. }
  8. //Animal.java
  9. public class Animal {
  10. public String name;
  11. public Animal(String name) {
  12. this.name = name;
  13. }
  14. public void printName() {
  15. System.out.println("Animal ["+name+"]");
  16. }
  17. }

第一步(编译): 在创建源文件之后,编译程序。.class文件。Java在编译类时,如果类所依赖的类尚未编译,编译器将首先编译依赖类,然后再引用它。否则,将被直接引用。这有点像make。如果java编译器在指定目录下找不到类所依赖的类。.class文件或者.java如果使用源文件,则编译器会说“cant find symbol“错误。

编译后的字节码文件格式分为两个主要部分: 常量池方法字节码 。常量池记录了代码出现的所有内容。token(类名、成员变量名等。)和符号引用(方法引用、成员变量引用等)。下面是MainApp.class通过分解结果,我们可以清楚地看到.class文件的结构:

图2  MainApp类常量池

图3  MainApp类方法字节码

最后生成的class该文件由以下部分组成:

  • 结构信息。包括class文件格式版本号以及关于每个部分的数量和大小的信息。
  • 元数据。对应Java源代码中声明的常量信息。包含类/继承的超类/声明信息、域和方法声明信息以及实现的常量接口池。
  • 方法信息。通信Java与源代码中的语句和表达式对应的信息。包含字节码、异常处理器表、求值堆栈和局部变量区域大小、求值堆栈类型记录、调试符号信息。

步骤2(运行):java办一门课的过程大致可以分为两个过程:1,类的加载  2、类执行。应该注意的是:JVM主要是当程序第一次主动使用类时,才会加载类。那是,JVM它不是在一开始就将程序加载到内存中,而是在必须使用时只加载一次。

以下是该程序运行的详细步骤:

  1. 在编译好java程序得到MainApp.class在文件之后,在命令行中键入。java AppMain。系统将启动一个jvm进程,jvm进程从classpath在路径中找到了一个名称。AppMain.class的二进制文件的MainApp调用将类信息加载到运行时数据区域的方法区域的过程。MainApp类加载。
  2. 然后JVM找到AppMain主函数入口,开始执行。main函数。
  3. main该函数的第一个命令是Animal  animal = new Animal("Puppy");就是让JVM创建一个Animal对象,但此时不在方法区域中。Animal类信息,因此JVM马上加载Animal类,把Animal类的类型信息放在方法区域中。
  4. 加载完Animal类之后,Java虚拟机要做的第一件事就是在堆区域创建一个新的。Animal实例分配内存, 然后调用构造函数进行初始化。Animal实例,则此Animal该实例持有一个指向方法区域的指针。Animal类的类型信息(它包含方法表,java动态绑定的底层实现)。
  5. 当使用animal.printName()的时候,JVM根据animal引用找到Animal对象,然后基于Animal对象持有的引用位于方法区域中。Animal获取类的类型信息的方法表。printName()函数的字节码的地址。
  6. 开始运行printName()函数。

图4   java程序操作的特殊说明:java类中所有public和protected所有的实例方法都使用动态绑定机制、所有私有方法、静态方法、构造函数和初始化方法。使用静态绑定机制。但是,在使用动态绑定机制时,将使用方法表,不会使用静态绑定。

Ps:

方法重载 :这在编译时发生。方法重载也称为编译时多态,因为编译器可以根据参数类型选择要使用的方法。

1

2

3

4

public class {

public static void evaluate(String param1); // method #1

public static void evaluate( int param1); // method #2

}

如果编译器要编译以下语句:

1

evaluate(“My Test Argument passed to param1”);

它根据传入参数为字符串常量生成调用。#1该方法的字节码。

方法覆盖 :这在运行时发生。方法重载之所以称为运行时多态,是因为编译器不知道也不知道在编译时调用哪个方法。JVM该决定是在代码运行时做出的。

1

2

3

4

5

6

7

8

9

10

11

12

public class A {

public int compute( int input) { //method #3

return 3 * input;

}

}

public class B extends A {

@Override

public int compute( int input) { //method #4

return 4 * input;

}

}

子类B中的compute(..)方法重写父类的compute(..)方法。如果编译器遇到以下代码:

1

2

3

public int evaluate(A reference, int arg2)  {

int result = reference.compute(arg2);

}

编译器无法知道传入的参数reference的类型是A还是B。因此,只有在运行时,才会根据分配的输入变量“reference“对象的类型(例如,A或者B实例)以决定调用该方法。#3还是方法#4.

版权声明

所有资源都来源于爬虫采集,如有侵权请联系我们,我们将立即删除