java基础-程序编译和运行的过程

一、java源文件执行过程:

 - 编译:
     java源程序会首先被java的编译器编译成.class文件,java编译器编译一个类时,首先会查看这个类依赖的类有没有编译过 ,如果依赖的类还没有被编译,编译器会首先编译这个类所依赖的类,然后再引用。如果已经是编译好的.class文件就直接引用。如果 java编译器在指定的目录找不到该类依赖的类的.java源文件或者.class文件,编译器话报“cant find symbol”的错误。
 - 运行:
java类的运行大致分为两个过程,1、类的加载,2、类的执行,jvm在程序第一次主动使用该类时,才会去加载该类。也就是说jvm并不是一开始就把所有的类都加载到内存中,而是不得不使用的时候才把它加载进来,而且只加载一次。

java基础-程序编译和运行的过程

通过如下的java代码解释java程序的执行过程:

//MainApp.java
public class MainApp{
	public static void main(String[] args){
		Animal animal = new Animal("gouzi");
		animal.printName();
	}
}
//Animal.java
public class Animal{
	String name;
	public Animal(String name){
		this.name = name;
	}
	public void printName(){
		System.out.println("这个狗的名字是"+name);
	}
}

.java文件编译后的字节码主要分为两部分常量池方法字节码(下图是MainApp.class通过反汇编的结果,我们可以清楚看到.class文件的结构)。

常量池:记录的是代码出现过的所有token(类名,成员变量名等等)以及符号引用(方法引用,成员变量引用等等)。

java基础-程序编译和运行的过程

方法字节码:方法字节码放的是类中各个方法的字节码。

java基础-程序编译和运行的过程

二、java中的堆、栈、方法区

java基础-程序编译和运行的过程

堆区: 
1.存储的全部是对象,每个对象都包含一个与之对应的class的信息。(class的目的是得到操作指令) 
2.jvm只有一个堆区(heap)被所有线程共享,堆中不存放基本类型和对象引用,只存放对象本身 
栈区: 
1.每个线程包含一个栈区,栈中只保存基础数据类型的对象和自定义对象的引用(不是对象),对象都存放在堆区中 
2.每个栈中的数据(原始类型和对象引用)都是私有的,其他栈不能访问。 
3.栈分为3个部分:基本类型变量区、执行环境上下文、操作指令区(存放操作指令)。 
方法区: 
1.又叫静态区,跟堆一样,被所有的线程共享。方法区包含所有的class和static变量。 
2.方法区中包含的都是在整个程序中永远唯一的元素,如class,static变量。 
为了更清楚地搞明白发生在运行时数据区里的黑幕,我们来准备2个小道具(2个非常简单的小程序)。 
java基础-程序编译和运行的过程

具体执行过程如下:
1) 在上面java文件编译好之后,在命令行输入"java AppMain",这时系统就会启动一个jvm进程,jvm进程从classPath中找到一个AppMain.class的二进制文件,然后将AppMain的类信息加载到内存运行时数据区的方法区内,这个过程是AppMain类的加载。
2)  然后jvm找到AppMain的主函数入口main函数,开始执行 main函数。
3) main函数的第一条语句是 "Animal animal = new Animal("gouzi")",但这时方法区中没有Animal类的信息,所以jvm就会加载Animal类,把Animal类的类型信息放到方法区中。
4) 加载完Animal类后java虚拟机首先会在堆中为Animal实例分配内存,然后调用构造函数初始化实例,这个Animal实例持有指向方法区的Animal类型信息(其中包括方法表,java动态绑定的底层实现)的引用。
5) 当使用animal.printName()的时候jvm会根据animal指向找到Animal实例对象,然后根据实例对象持有的Animal类型信息的引用,定位到方法区中Animal类的类型信息的方法表,获得printName()方法的字节码地址
6) 开始执行printName()方法。