Java编译期和运行期&JVM转载
原创Java编译代码编译Java源代码编译器按如下方式完成流程图:
Java执行字节码的执行JVM执行引擎完成,流程图如下:
如下图,Java从源文件的创建到程序的运行有两个步骤:1源文件由编译器编译成字节码(ByteCode) 2,字节码java运行虚拟机解释。因为java这个程序既要经过编译,又要通过。JVM可以说,这个解释是有道理的。Java称为半解释性语言( "semi-interpreted" language)。
图1 java程序编译和运行过程
下面通过这个java过程,以说明java程序从编译到最终运行的整个过程。代码如下:
Java代码
- //MainApp.java
- public class MainApp {
- public static void main(String[] args) {
- Animal animal = new Animal("Puppy");
- animal.printName();
- }
- }
- //Animal.java
- public class Animal {
- public String name;
- public Animal(String name) {
- this.name = name;
- }
- public void printName() {
- System.out.println("Animal ["+name+"]");
- }
- }
第一步(编译): 在创建源文件之后,编译程序。.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它不是在一开始就将程序加载到内存中,而是在必须使用时只加载一次。
以下是该程序运行的详细步骤:
- 在编译好java程序得到MainApp.class在文件之后,在命令行中键入。java AppMain。系统将启动一个jvm进程,jvm进程从classpath在路径中找到了一个名称。AppMain.class的二进制文件的MainApp调用将类信息加载到运行时数据区域的方法区域的过程。MainApp类加载。
- 然后JVM找到AppMain主函数入口,开始执行。main函数。
- main该函数的第一个命令是Animal animal = new Animal("Puppy");就是让JVM创建一个Animal对象,但此时不在方法区域中。Animal类信息,因此JVM马上加载Animal类,把Animal类的类型信息放在方法区域中。
- 加载完Animal类之后,Java虚拟机要做的第一件事就是在堆区域创建一个新的。Animal实例分配内存, 然后调用构造函数进行初始化。Animal实例,则此Animal该实例持有一个指向方法区域的指针。Animal类的类型信息(它包含方法表,java动态绑定的底层实现)。
- 当使用animal.printName()的时候,JVM根据animal引用找到Animal对象,然后基于Animal对象持有的引用位于方法区域中。Animal获取类的类型信息的方法表。printName()函数的字节码的地址。
- 开始运行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.
版权声明
所有资源都来源于爬虫采集,如有侵权请联系我们,我们将立即删除
itfan123



