JVM之java源码编译机制

前言:

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应用 基础与实践







评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值