在讲JVM最开始,我们先以一个简单的Java程序的运行开始讲,JAVA程序的运行原理。下面我先把我们实例程序列出来,我们该实例程序一共有两个java文件:Hello.java和Person.java:

Hello.java文件:

public class Hello { public static void main(String[] args) { Person p = new Person(); }}

Person.java文件

public class Person { private String name;}

首先在IDE中编写java源文件,文件名以.java结束。编写完源文件,然后使用javac命令把源文件编译成以.class结尾的二进制文件或者打包为jar文件。

当我们需要去执行我们编写的java程序的时候,如果是直接编译后的.class文件,那么使用:

java com.xx.Hello.class

java *.class命令去运行包含了main方法的那个.class文件。如果我们是以jar的方式进行运行java程序,那么执行 :

java -jar *.jar com.xx.Hello

起中 Hello是这个jar中包含了main方法的类的名称。

无论是那种方式,当我们执行的时候,就会启动jvm虚拟机去加载所要执行的Hello.class文件到虚拟机中。然后在jvm中有字节码执行引擎负责去执行Hello.class中的main方法,在main方法中使用到了Person类,此时jvm又会去加载Person。也就是说jvm用到哪个类,然后就去加载哪个类。下面用图来描述一下java程序的执行过程:




java如何运行TGame java如何运行多个类_接口多个实现类加载哪个

java执行过程



1.执行java命令启动jvm,通过类加载器Hello.class加载到jvm中

2.jvm中的字节码执行引擎执行Hello.class中入口方法main方法

3.由于main使用了Person类,然后jvm类加载器再去加载Person类。

4.最后字节码执行引擎执行Person.class。

##类的加载过程

通过上面的结束,基本了解了java程序的一个大致的执行过程,在上面描述的加载过程中,其中一个最重要的环节是类的加载。下面接受一下类是如何加载的。

在Java类被加载到虚拟机到从内存中卸载,整个生命周期包括以下部分:

加载,连接,初始化,使用,卸载

在java中的所有的类型都是在运行的过程中进行加载,连接和初始化。

类的加载过程分为三个部分:

加载:把class文件加载到内存中

连接:分为三个部分:验证,准备,解析。

初始化:类加载最后一步,对类中的变量进行赋值,在代码层面是就是执行用户在类中的定义的赋值语句。

###加载

类的加载,包括从本地中读取.class文件,也可以从网络中读取二进制字节流信息。

###连接

其中连接又分为三个部分:

验证: 主要是包括文件格式验证,元数据验证,字节码验证

准备: 准备阶段是为类分配内存空间。

解析: 将虚拟机常量池内的符合引用替换为直接引用的过程,这部分比较复杂以后会详细说。

###初始化

类的初始化虚拟机规范规定:只有在5种情况下才会发生:

1.执行new 对象操作、读取活设置一个类的静态字段(不包括用final修饰过的,因为final修饰的属性已经在编译器被放到常量池中)、调用一个类的静态方法。

2.使用reflect包的方法对类进行反射调用的时候。

3.当初始化一个类的时候,如果父类没有初始化,先完成对父类的初始化。

4.当JVM启动的时候,需要指定一个执行main方法的主类,那么虚拟机会首先初始化这个主类。

5.JDK 7 中使用动态语言支持时,如果一个 java.lang.invoke.MethodHandle 实例正好是对 REF_getStatic, REF_putStatic, REF_invokeStatic 进行方法句柄解析的结果时。

>1.对于接口的初始化,对于子接口与父接口的规则与java类不一样,子接口的初始化并不要求父接口全部都完成了初始化,只有在真正使用到父接口的时候,才会对父接口进行初始化。

2.如果接口中定义了默认实现方法,那么当实现这个接口的类初始化的时候,也会除非这个接口的初始化。

以上五点被称为主动使用,只有主动使用的时候,类才会被初始化。而被动使用是不会引起类的初始化的。类的被动使用又主要包括下面三种情况:

* 通过子类引用父类的静态属性,父类初始化,但是子类不会被初始化

* 定义某个类的数组,该类不会初始化

* 使用类修饰的final的属性,也不会引起类初始化。