java execution flow analysis method

Java program is running, must be compiled and run in two steps. First, the source file name suffix to compile .java ultimately produce .class suffix of the file named bytecode. Then the compiled Java Virtual Machine byte code file is loaded into memory results (this process is called the class loader, the loader is done), then the virtual machine for interpreting and executing the java class is loaded into memory, displayed.

Java operating principle

It introduced the concept of a virtual machine in Java, namely between the machine and compiler adding a layer of abstraction of virtual machines. This virtual machine on any platform are available to the compiler of a common interface. The compiler for a virtual machine only need to generate code can be appreciated that the virtual machine, and then by the interpreter to convert the virtual machine code for a particular machine code execution system. In Java, the code for this is called the virtual machine bytecode appreciated (the ByteCode), which does not face to any particular processor, only for the virtual machine. Each platform interpreter is different, but the implementation of the virtual machine is the same. After the Java source code compiler becomes bytecode, byte code interpreted by the virtual machines, each virtual machine bytecode to be executed to the interpreter, the interpreter will translate it into machine on a particular machine code, and then run on a particular machine.

The whole process of compiling and executing Java code

Java code is compiled Java source code is accomplished by a compiler, the flowchart as shown below:

Detailed program execution process java

Performs Java bytecode is done by the JVM execution engine, the flowchart as shown below:

Detailed program execution process java

Java code compilation and execution of the entire process includes the following three important mechanisms:

Java source code compilation mechanism

Class loading mechanism

Class implementation mechanisms

Java source code compilation mechanism

Compile Java source code consists of the following three processes: (javac -verbose output messages on the operation being performed by the compiler)

Analysis input to the symbol table

Annotation Processing

Semantic analysis and generate class files

Detailed program execution process java

Last generated class file consists of the following components:

1, the structure of information. Including information on the number and size of each part of the class file format version

2, the metadata. Java source code corresponding to the information statement and constants. Containing the class / superclass inheritance / implementation of the declaration of the interface information, domain information and method declaration and constant pool

3, method information. The corresponding Java source code statements and expressions corresponding information. Comprising bytecodes, an exception handler table, the size of the evaluation stack with local variables, the type of recording evaluation stack, debug symbol information

Class loading mechanism

JVM的类加载是通过ClassLoader及其子类来完成的,类的层次关系和加载顺序可以由下图来描述:

Detailed program execution process java

1)Bootstrap ClassLoader /启动类加载器

$JAVA_HOME中jre/lib/rt.jar里所有的class,由C++实现,不是ClassLoader子类

2)Extension ClassLoader/扩展类加载器

负责加载java平台中扩展功能的一些jar包,包括$JAVA_HOME中jre/lib/*.jar或-Djava.ext.dirs指定目录下的jar包

3)App ClassLoader/ 系统类加载器

负责记载classpath中指定的jar包及目录中class

4)Custom ClassLoader/用户自定义类加载器(java.lang.ClassLoader的子类)

属于应用程序根据自身需要自定义的ClassLoader,如tomcat、jboss都会根据j2ee规范自行实现ClassLoader

加载过程中会先检查类是否被已加载,检查顺序是自底向上,从Custom ClassLoader到BootStrap ClassLoader逐层检查,只要某个classloader已加载就视为已加载此类,保证此类只所有ClassLoader加载一次。而加载的顺序是自顶向下,也就是由上层来逐层尝试加载此类。

类加载双亲委派机制介绍和分析

在这里,需要着重说明的是,JVM在加载类时默认采用的是双亲委派机制。通俗的讲,就是某个特定的类加载器在接到加载类的请求时,首先将加载任务委托给父类加载器,依次递归,如果父类加载器可以完成类加载任务,就成功返回;只有父类加载器无法完成此加载任务时,才自己去加载。

类执行机制

JVM是基于栈的体系结构来执行class字节码的。线程创建后,都会产生程序计数器(PC)和栈(Stack),程序计数器存放下一条要执行的指令在方法内的偏移量,栈中存放一个个栈帧,每个栈帧对应着每个方法的每次调用,而栈帧又是有局部变量区和操作数栈两部分组成,局部变量区用于存放方法中的局部变量和参数,操作数栈中用于存放方法执行过程中产生的中间结果。

 

 

public static void main(String[] args){

Student s = new Student(23,“dqrcsc”,“20150723”);//执行完毕

s.study(5,6);

Student.getCnt();

s.run();

}

执行过程如下

1、为main方法创建栈帧:

Detailed program execution process java

局部变量表长度为2,slot0存放参数args,slot1存放局部变量Student s,操作数栈最大深度为5。

2、new#7指令,在java堆中创建一个Student对象,并将其引用值放入栈顶。

Detailed program execution process java

3、初始化一个对象(通过实例构造的方式)

up指令:复制栈顶的值,然后将复制的结果入栈。

bipush 23:将单字节常量值23入栈。

ldc #8:将#8这个常量池中的常量即”dqrcsc”取出,并入栈。

ldc #9:将#9这个常量池中的常量即”20150723”取出,并入栈。

Detailed program execution process java

4、invokespecial #10:调用#10这个常量所代表的方法,即Student.()这个方法,这步是为了初始化对象s的各项值

《init》()方法,是编译器将调用父类的《init》()的语句、构造代码块、实例字段赋值语句,以及自己编写的构造方法中的语句整合在一起生成的一个方法。保证调用父类的《init》()方法在最开头,自己编写的构造方法语句在最后,而构造代码块及实例字段赋值语句按出现的顺序按序整合到《init》()方法中。

Detailed program execution process java

注意到Student.《init》()方法的最大操作数栈深度为3,局部变量表大小为4。

此时需注意:从dup到ldc #9这四条指令向栈中添加了4个数据,而Student.()方法刚好也需要4个参数:

public Student(int age, String name, String sid){

super(age,name);

this.sid = sid;

}

虽然定义中只显式地定义了传入3个参数,而实际上会隐含传入一个当前对象的引用作为第一个参数,所以四个参数依次为this,age,name,sid。

上面的4条指令刚好把这四个参数的值依次入栈,进行参数传递,然后调用了Student.《init》()方法,会创建该方法的栈帧,并入栈。栈帧中的局部变量表的第0到4个slot分别保存着入栈的那四个参数值。

创建Studet.《init》()方法的栈帧:

Detailed program execution process java

Student.《init》()方法中的字节码指令:

Detailed program execution process java

aload_0:将局部变量表slot0处的引用值入栈

aload_1:将局部变量表slot1处的int值入栈

aload_2:将局部变量表slot2处的引用值入栈

Detailed program execution process java

invokespecial #1:调用Person.()方法,同调用Student.过程类似,创建栈帧,将三个参数的值存放到局部变量表等,这里就不画图了……

从Person.()返回之后,用于传参的栈顶的3个值被回收了。

aload_0:将slot0处的引用值入栈。

aload_3:将slot3处的引用值入栈。

Detailed program execution process java

putfield #2:将当前栈顶的值”20150723”赋值给0x2222所引用对象的sid字段,然后栈中的两个值出栈。

return:返回调用方即main()方法,当前方法栈帧出栈。

重新回到main()方法中,继续执行下面的字节码指令:

astore_1:将当前栈顶引用类型的值赋值给slot1处的局部变量,然后出栈。

Detailed program execution process java

5、到这儿为止,第一行代码执行完毕,将s返回给局部变量表,执行下边的

aload_1:slot1处的引用类型的值入栈

iconst_5:将常数5入栈,int型常数只有0-5有对应的iconst_x指令

bipush 6:将常数6入栈

Detailed program execution process java

6、开始执行第二行代码,也就是strudy方法

invokevirtual #11:调用虚方法study(),这个方法是重写的接口中的方法,需要动态分派,所以使用了invokevirtual指令。

创建study()方法的栈帧:

Detailed program execution process java

最大栈深度3,局部变量表5

Detailed program execution process java

方法的java源码:

这里写代码片public int study(int a, int b){

int c = 10;

int d = 20;

return a+b*c-d;

}123456789

Detailed program execution process java

bipush 10:将10入栈

istore_3:将栈顶的10赋值给slot3处的int局部变量,即c,出栈。

bipush 20:将20入栈

istore 4:将栈顶的20付给slot4处的int局部变量,即d,出栈。

上面4条指令,完成对c和d的赋值工作。

iload_1、iload_2、iload_3这三条指令将slot1、slot2、slot3这三个局部变量入栈:

Detailed program execution process java

imul:将栈顶的两个值出栈,相乘的结果入栈:

Detailed program execution process java

iadd:将当前栈顶的两个值出栈,相加的结果入栈

iload 4:将slot4处的int型的局部变量入

Detailed program execution process java

isub:将栈顶两个值出栈,相减结果入栈:

ireturn:将当前栈顶的值返回到调用方。

Detailed program execution process java

7、到这儿为止,第二行代码执行完毕,返回值返回给s,执行下边的

public static void main(String[] args){

Student s = new Student(23,“dqrcsc”,“20150723”);//执行完毕

s.study(5,6);

Student.getCnt();

s.run();

}1234567891011

invokestatic #12 调用静态方法getCnt()不需要传任何参数

pop:getCnt()方法有返回值,将其出栈

aload_1:将slot1处的引用值入栈

invokevirtual # 13: call run () method 0x2222 object, a method of rewriting from the parent class, dynamically assigned, so use instructions invokevirtual

return: main () returns, the program ends.

Guess you like

Origin www.cnblogs.com/minikobe/p/11512628.html