您的位置:首页 > 编程语言 > Java开发

JVM之java源码编译机制

2017-10-22 10:06 344 查看

前言:

java程序运行在JVM之上,JVM的运行状况对于java程序会产生很大的影响,所以掌握JVM中关键的机制对与编写稳定的,高性能的java程序至关重要!

JVM标准结构图



JVM负责装载class文件并执行,首先要掌握以下三个问题

1.JDK是如何将java代码编译为class文件的?

2.如何装载class文件?

3.如何执行class?

将源码编译class文件的实现取决于各个JVM实现或 各种源码编译器

class文件通常由类加载器classLoader来完成加载

class的执行在SUN JDK中有解释执行和编译为机器码执行两种方式。

本文主要详细介绍java源码编译机制

java源码编译机制

JVM规范中定义了class文件的格式,但没有规定java源码如何编译为class文件,各厂商都会实现将符合java语言规范的源码编译为class文件的编译器,
如Sun JdK中就是javac,javac将源码编译为class文件的步骤如图所示



下面简单介绍三个步骤

1.分析和输入到符号表(Parse and Enter)

Parse所做的为词法和语法分析
   词法分析:
将代码字符串转变为token序列
 token就是把程序的语句进行类似分词得到的单词
    语法分析:
根据语法由tiken序列生成抽象语法树
Enter
做的是将符号输入到符号表
通常包括确定类的超类型和接口,根据需要添加默认构造器,将类中出现的符号输入类自身的符号表中去

2.注解处理(Annotation Processing)

该步骤主要用于处理用户自定义的注解 ,如以下代码
import lombok.Getter;

public class User{
private @Getter String name;
public static void main(String args[]){
String mango="mango";
String s="abc"+mango+"def";
System.out.println(s);

}
}
编译时引入Lombok对User.java进行编译后,可以通过javap查看class文件自动生成了public String getUsername()方法

javac -cp lombok-1.16.16.jar User.java



javap User



关于Lombok 看考http://blog.csdn.net/ghsau/article/details/52334762
在Annotation Processing进行后,再次进入了Pare and Enter步骤

3.语义分析和生成class文件(Analyse and Generate)

Analyse语义分析:基于抽象语法树进行一系列的语义分析
1.包括将树中的名字,表达式等变量,方法,类型联系到一起
2.检查变量使用前是否已声明
3.检查checked exception都被捕获或抛出
4.将泛型java转为普通java
5.将含有语法糖的语法树改为含有简单语法的语言结构的的语法树,如for exch,自动装箱拆箱等
.......
完成了语义分析后,就开始生成class文件。

生成的步骤为
1.将实例成员初始化器收集到构造器中
2.将静态成员初始化器收集为《clinit》
3.将抽象语法树生成字节码,采用的方法为后续遍历 语法树
4.进行最后的少量代码转换(例如string相加转变为stringBuilder相加)
5.从符号表生成class文件

除了javac外,还可以通过Eclipse Complier for java 或jikes等编译器来讲java文件编译为class文件

class文件结构

class文件结构不仅仅存放了字节码,还包含了很多辅助jvm来执行的clasas附加信息,一个class文件包含了以下信息

结构信息

包含class文件格式版本后及各部分的数量与大小信息

元数据

java源码中声明与常量的信息,如类、超类,实现的接口声明的信息,域field与方法声明信息和常量池

方法信息

java源码语句与表达式对应的信息
以一段简单的代码来说明class文件结构
public class Foo {
private static final int MAX_COUNT = 1000;
private static int count = 0;
public int bar() throws Exception{
if(++count<MAX_COUNT){
count = 0;
throw new Exception("count overflow");
}
return count;
}

}
javac -g Foo.java 
编译后,使用
javap -c -s -l -verbose Foo 查看编译后的class文件

E:\test>javap -c -s -l -verbose Foo
Classfile /E:/test/Foo.class
Last modified 2017-10-22; size 607 bytes
MD5 checksum f7e8e8b3a4f1f3a7acefb5253d8f43df
Compiled from "Foo.java"
//元数据:类/继承的超类/实现的接口的声明信息
public class Foo
minor version: 0
//50表示jdk 6  ,51表示jdk 7,   52表示jdl 8
major version: 52
flags: ACC_PUBLIC, ACC_SUPER
//元数据:常量池
Constant pool:
#1 = Methodref          #7.#27         // java/lang/Object."<init>":()V
#2 = Fieldref           #3.#28         // Foo.count:I
#3 = Class              #29            // Foo
#4 = Class              #30            // java/lang/Exception
#5 = String             #31            // count overflow
#6 = Methodref          #4.#32         // java/lang/Exception."<init>":(Ljava/lang/String;)V
#7 = Class              #33            // java/lang/Object
#8 = Utf8               MAX_COUNT
#9 = Utf8               I
#10 = Utf8               ConstantValue
#11 = Integer            1000
#12 = Utf8               count
#13 = Utf8               <init>
#14 = Utf8               ()V
#15 = Utf8               Code
#16 = Utf8               LineNumberTable
#17 = Utf8               LocalVariableTable
#18 = Utf8               this
#19 = Utf8               LFoo;
#20 = Utf8               bar
#21 = Utf8               ()I
#22 = Utf8               StackMapTable
#23 = Utf8               Exceptions
#24 = Utf8               <clinit>
#25 = Utf8               SourceFile
#26 = Utf8               Foo.java
#27 = NameAndType        #13:#14        // "<init>":()V
#28 = NameAndType        #12:#9         // count:I
#29 = Utf8               Foo
#30 = Utf8               java/lang/Exception
#31 = Utf8               count overflow
#32 = NameAndType        #13:#34        // "<init>":(Ljava/lang/String;)V
#33 = Utf8               java/lang/Object
#34 = Utf8               (Ljava/lang/String;)V
{
//Enter:将符号输入到符号表是生成的默认构造器方法
public Foo();
descriptor: ()V
flags: ACC_PUBLIC
Code:
stack=1, locals=1, args_size=1
0: aload_0
1: invokespecial #1                  // Method java/lang/Object."<init>":()V
4: return
LineNumberTable:
line 1: 0
LocalVariableTable:
Start  Length  Slot  Name   Signature
0       5     0  this   LFoo;
//bar方法的元数据信息
public int bar() throws java.lang.Exception;
descriptor: ()I
flags: ACC_PUBLIC
Code:
stack=3, locals=1, args_size=1
//方法对应的字节码信息
0: getstatic     #2                  // Field count:I
3: iconst_1
4: iadd
5: dup
6: putstatic     #2                  // Field count:I
9: sipush        1000
12: if_icmpge     29
15: iconst_0
16: putstatic     #2                  // Field count:I
19: new           #4                  // class java/lang/Exception
22: dup
23: ldc           #5                  // String count overflow
25: invokespecial #6                  // Method java/lang/Exception."<init>":(Ljava/lang/String;)V
28: athrow
29: getstatic     #2                  // Field count:I
32: ireturn
//行号信息
LineNumberTable:
line 5: 0
line 6: 15
line 7: 19
line 9: 29
//局部变量信息
LocalVariableTable:
Start  Length  Slot  Name   Signature
0      33     0  this   LFoo;
StackMapTable: number_of_entries = 1
frame_type = 29 /* same */
//异常处理器表
Exceptions:
throws java.lang.Exception

static {};
descriptor: ()V
flags: ACC_STATIC
Code:
stack=1, locals=0, args_size=0
0: iconst_0
1: putstatic     #2                  // Field count:I
4: return
LineNumberTable:
line 3: 0
}
SourceFile: "Foo.java"


从上可见,class文件是个完整的自描述文件,字节码在其中只占了很小的一部分。

参考:分布式java应用 基础与实践
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: