jvm详解-java源码编译

5 篇文章 0 订阅

http://blog.csdn.net/larrylgq/article/details/7395261

java源码级编译器的任务是将符合java语言规范的源码编译为符合jvm规范的Class文件,对不符合java语言规范的报错

在sun的jdk中源码编译器是使用java写的javac

javac的工作流程:

1 解析(parse)和输入到符号表(enter)

2 注解处理(annotation processing)

3 分析与代码的生成

解析:

词法分析:使用com.sun.tools.javac.parser.Scanner类,将代码字符串变为Token序列

eg:

int y = 1;

->Token.INT(name:int)->Token.IDENTIFIER(name:y))+ Token.EQ(name:=))+ Token.INTLITERAL(stringVal:1))+ Token.SEMI(name:;))

语法分析:是一个递归下降且运算符优先级的语法分析器,使用com.sun.tools.javac.parser.Parser类,根据语法由Token序列生成抽象语法树

eg:

 

 

输入到符号表

使用com.sun.tools.javac.comp.Enter类,此过程每个编译单元的抽象语法树的顶局节点都先被放到待处理列表中并逐个处理列表中的节点

所有的类符号被输入到外围作用域的符号表中

确定类的参数(对泛型类型而言)、超类型和接口

如果需要添加默认构造器

将类中出现的符号输入到类自身的符号表中

分析和检查代码中的annotation

 

注解处理:annotation processing

使用com.sun.tools.javac.processing.JavacProcessingEnvironment类,并且支持用户自定义的annotation

编译时lombook对java文件进行编译之后会再次进入Parse and Enter步骤

 

标注(Attr)和检查(Check):

是语义分析的一个步骤,使用com.sun.tools.javac.comp.Attr类和com.sun.tools.javac.comp.Check类

主要作用有:

将语法树中名字、表达式等元素与变量、方法、类型等联系到一起
检查变量使用前是否已声明
推导泛型方法的类型参数
检查类型匹配性
进行常量折叠(将常量语法树合并)

数据流分析(Flow)

使用com.sun.tools.javac.comp.Flow类

检查所有语句都可到达

检查所有checked exception都被捕获或抛出
检查所有局部变量在使用前必项确定性赋值

 检查有返回值的方法必须确定性返回值

检查变量的确定性且不重复赋值

转换类型:(TransTypes)

使用com.sun.tools.javac.comp.TransTypes主要作用是将泛型java代码转换成普通的java代码

eg:

List<Integer> list = Arrays.asList(1, 2, 3);
int i = list.get(0);

转换后:

List list = Arrays.asList(1, 2, 3);
int i = (Integer)list.get(0);为了保证泛型的语义,增加了强制类型转换

 

解除语法糖:(Lower)

使用com.sun.tools.javac.comp.Lower类

消除if(false){...}形式的无用代码

将含有语法糖的语法树改写为含有简单语言结构的语法树

eg

 具名内部类,匿名内部类,类字面量

自动装箱拆箱

断言

foreach循环

enum或String类型的switch(java7)

eg:

List<Integer> list = Arrays.asList(1, 2, 3);
int i = list.get(0);

转换后:

List list = Arrays.asList(//自动装箱拆箱
new Integer[]{
Integer.valueOf(1),
Integer.valueOf(2),
Integer.valueOf(3)
}
);
int i = ((Integer)list.get(0)).intValue();

生成class文件:(Gen)

使用com.sun.tool.javac.jvm.Gen

将实例成员初始化器收集到构造器中成为<init>()
将静态成员初始化器收集为<clinit>()
从抽象语法树生成字节码
后序遍历语法树
进行最后的少量代码转换
    String的+操作被生成为StringBuilder操作
    x++/x--在条件允许时被优化为++x/--x
    etc …
从符号表生成Class文件
    生成Class文件的结构信息
    生成元数据(包括常量池)

class文件所记录的信息

结构信息

    Class文件格式版本号

    各部分的数量及大小

元数据

    类、继承的超类、实现接口的声明信息

    域与方法声明信息

    常量池

    用户自定义的、RetentionPolicy为CLASS或RUNTIME的注解

方法信息

    字节码(程序逻辑)

    异常处理器表

   操作数栈与局部变量区大小

    操作数栈的类型记录(java6开始 StackMapTable)

    调试用符号信息(LineNumberTable,LocalVariableTable)

 

class文件例子:

import java.io.Serializable;
public class Foo implements Serializable {
    public void bar() {
        int i = 31;
        if (i > 0) {
            int j = 42;
        }
    }
}

执行编译:javac -g Foo.java

执行反编译:javap -c -s -l -verbose Foo

类声明:

public class Foo extends java.lang.Object implements java.io.Serializable

源文件名:

SourceFile: "Foo.java"

Class文件结构信息:

minor version: 0
major version: 50

常量池:

Constant pool:
const #1 = Method #3.#19; // java/lang/Object."<init>":()V
const #2 = class #20; // Foo
const #3 = class #21; // java/lang/Object
const #4 = class #22; // java/io/Serializable
const #5 = Asciz <init>;
const #6 = Asciz ()V;
const #7 = Asciz Code;
const #8 = Asciz LineNumberTable;
const #9 = Asciz LocalVariableTable;
const #10 = Asciz this;
const #11 = Asciz LFoo;;
const #12 = Asciz bar;
const #13 = Asciz j;
const #14 = Asciz I;
const #15 = Asciz i;
const #16 = Asciz StackMapTable;
const #17 = Asciz SourceFile;
const #18 = Asciz Foo.java;
const #19 = NameAndType #5:#6;// "<init>":()V
const #20 = Asciz Foo;
const #21 = Asciz java/lang/Object;
const #22 = Asciz java/io/Serializable;//引入包时使用*通配,从Class文件来看没有任何不同,只不过使用通配符增加了源码中名字冲突的可能性而已。

方法元数据:

public Foo();
    Signature: ()V
    LineNumberTable:
        line 2: 0
    LocalVariableTable:
        Start Length Slot Name Signature
        0 5 0 this LFoo;
    Code:
        Stack=1, Locals=1, Args_size=1

字节码:

0: aload_0
1: invokespecial #1; //Method java/lang/Object."<init>":()V
4: return

方法元数据:

public void bar();
    Signature: ()V
    LineNumberTable:
        line 4: 0
        line 5: 3
        line 6: 7
        line 8: 10
    LocalVariableTable:                        // 参数名跟局部变量名都保存在LocalVariableTable;返个属性表跟方法体联系在一起,而接口的方法没有方法体,
    Start Length Slot Name Signature //所以ASM无法从接口类型上的方法取得参数的名称
    10 0 2 j I
    0 11 0 this LFoo;
    3 8 1 i I
StackMapTable: number_of_entries = 1
    frame_type = 252 /* append */
        offset_delta = 10
        locals = [ int ]
Code:
Stack=1, Locals=3, Args_size=1

字节码:

0: bipush 31
2: istore_1
3: iload_1
4: ifle 10
7: bipush 42
9: istore_2
10: return

 

Class文件与jdk版本比较

JDK版本Class文件版本(major.minor)
1.0 --------45.3
1.1 --------45.3
1.2 --------46.0
1.3 --------47.0
1.4 --------48.0
5    --------49.0
6    --------50.0
7    --------51.0

 

  • 1
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值