深入理解java虚拟机第三版读书笔记10

以下是第十章 前端编译与优化的内容

Java中的编译可能是指:

  • 前端编译:把*.java文件转变成*.class文件的过程
  • 即时编译(JIT):运行期把字节码转变成本地机器码的过程
  • 提前编译(AOT):直接把程序编译成与目标机器指令集相关的二进制代码的过程

这一章讲的是前端编译

Javac编译器

javac是一个由java语言编写的程序

Javac的源码与调试

Java6以后,Javac的源码放在了JDK_SRC_HOME/langtools/src/share/classes/com/sun/tools/javac

Javac的工作流程

  1. 准备过程:初始化插入式注解处理器。
  2. 解析与填充符号表过程,包括:
    ·词法、语法分析。将源代码的字符流转变为标记集合,构造出抽象语法树。
    ·填充符号表。产生符号地址和符号信息。
  3. 插入式注解处理器的注解处理过程:插入式注解处理器的执行阶段。
  4. 分析与字节码生成过程,包括:
    • 标注检查。对语法的静态信息进行检查。
    • 数据流及控制流分析。对程序动态运行过程进行检查。
    • 解语法糖。将简化代码编写的语法糖还原为原有的形式。
    • 字节码生成。将前面各个步骤所生成的信息转化成字节码。

执行插入式注解时又可能会产生新的符号,如果有新的符号产生,就必须转回到之前的解析、填充符号表的过程中重新处理这些新符号。

Java语法糖的味道

泛型

Java与C#的泛型

Java是 “类型擦除式泛型”,C#是 “具现化式泛型”

Java语言中的泛型只在程序源码中存在,在编译后的字节码文件中,全部泛型都被替换为原来的裸类型,并且在相应的地方插入了强制转型代码。

Java的类型擦除式泛型无论在使用效果上还是运行效率上,几乎是全面落后于C#的具现化式泛型,而它的唯一优势是在于实现这种泛型的影响范围上。

Java选择类型擦除主要是因为遗留代码多,向前兼容。

类型擦除

实现:泛型类原地泛型化变成裸类型

Java是简单粗暴地直接在编译时把ArrayList<Integer>还原回ArrayList,只在元素访问、修改时自动插入一些强制类型转换和检查指令。

类型擦除的缺陷:

  1. 不支持基本类型,例如ArrayList<int>,因为无法实现int和Object互转,只能自动装箱、拆箱使用Integer,导致执行效率低。
  2. 运行期无法取到泛型类型信息,导致不能出现下列操作:
    • if (item instanceof E) { // 不合法,无法对泛型进行实例判断
    • E newItem = new E(); // 不合法,无法使用泛型创建对象
    • E[] itemArray = new E[10]; // 不合法,无法使用泛型创建数组
  3. 方法重载即使是不同泛型类型的参数也不能作为不一样的特征签名。但是返回值类型不同实际是可以正常运行的,虽然特征签名还是一致,但class文件支持不同描述符的方法共存,后续JVM有优化。

值类型和未来的泛型

在2014年,刚好是Java泛型出现的十年之后,Oracle建立了一个名为Valhalla的语言改进项目,希望改进Java语言留下的各种缺陷(解决泛型的缺陷就是项目主要目标其中之一)。

在Valhalla项目中规划了几种不同的新泛型实现方案,在这些新的泛型设计中,泛型类型有可能被具现化,也有可能继续维持类型擦除以保持兼容(取决于采用哪种实现方案),即使是继续采用类型擦除的方案,泛型的参数化类型也可以选择不被完全地擦除掉,而是相对完整地记录在Class文件中,能够在运行期被使用,也可以指定编译器默认要擦除哪些类型。相对于使用不同方式实现泛型,目前比较明确的是未来的Java应该会提供“值类型”(Value Type)的语言层面的支持。

值类型可以与引用类型一样,具有构造函数、方法或是属性字段,等等,而它与引用类型的区别在于它在赋值的时候通常是整体复制,而不是像引用类型那样传递引用的。更为关键的是,值类型的实例很容易实现分配在方法的调用栈上的,这意味着值类型会随着当前方法的退出而自动释放,不会给垃圾收集子系统带来任何压力。

在Valhalla项目中,Java的值类型方案被称为“内联类型”,计划通过一个新的关键字inline来定义。

条件编译

使用条件为常量的if语句,可以在编译期间过滤掉一些代码

public static void main(String[] args) {     
    if (true) {
        System.out.println("block 1");
    } else {
        System.out.println("block 2");
    }
}

反编译的结果

public static void main(String[] args) {
    System.out.println("block 1");
}

可变参数

public void foo(String... args){}

=> public void foo(String[] args){}

switch-字符串和switch-enum

switch-字符串

配合字符串的哈希值使用。

String i = "hello";
switch (i){
    case "hello":
        System.out.println("h");
        break;
    case "world":
        System.out.println("w");
        break;
}

反编译后

String i = "hello";
byte var3 = -1;
switch(i.hashCode()) {
case 99162322:
    if (i.equals("hello")) {
        var3 = 0;
    }
    break;
case 113318802:
    if (i.equals("world")) {
        var3 = 1;
    }
}

switch(var3) {
case 0:
    System.out.println("h");
    break;
case 1:
    System.out.println("w");
}

switch-enum

enum Sex{
    MALE,FEMALE
}

public static void foo(Sex sex){
    switch (sex){
        case MALE:
            System.out.println("男");
            break;
        case FEMALE:
            System.out.println("女");
            break;
    }
}

反编译后

/*
 *定义一个合成类(仅jvm使用,对我们不可见)
 *用来映射枚举的ordina1与数组元素的关系
 *枚举的ordinal表示枚举对象的序号,从0开始
 *即MALE的ordinal()=0,FEMALE的ordinal()=1
 */
static class $MAP{ 
    //数组大小即为枚举元素个数,里面存储case用来对比的数字 
    static int[] map = new int[2]; 
    static{ 
        map[Sex.MALE.ordinal()]=1; 
        map[Sex.FEMALE.ordinal()]=2; 
    }
}

public static void foo(Sex sex)
{ 
    int x = $MAP.map[sex.ordinal()]; 
    switch(x){ 
        case 1:
            System.out.println("男"); 
            break; 
        case 2:
            System.out.println("女"); 
            break;
    }
}

枚举

enum Sex{
    MALE,FEMALE
}

实际上是编译成了

public final class Sex extends Enum<Sex>{ 
    public static final Sex MALE; 
    public static final Sex FEMALE; 
    private static final Sex[] $VALUES;
    static{ 
        MALE = new Sex("MALE",0); 
        FEMALE = new Sex("FEMALE",1); 
        $VALUES = new Sex[]{MALE,FEMALE};
    }
    private Sex(String name,int ordinal){ 
        super(name,ordinal); 
    }
    public static Sex[] values(){ 
        return $VALUES.clone(); 
    }
    public static Sex valueOf(String name){ 
        return Enum.valueOf(Sex.class,name);
    }
}

try-with-resources

JDK7开始新增了对需要关闭的资源处理的特殊语法try-with-resources:

try(资源变量=创建资源对象){

}catch(){

}

其中资源对象需要实现Autocloseable接口,例如InputStream、OutputStream、Connection、Statement、Resultset等接口都实现了Autocloseable,使用try-with-resources可以不用写finally语句块,编译器会帮助生成关闭资源代码,例如:

public static void main(String[]args){ 
    try(InputStream is = new FileInputStream("d:\\1.txt")){ 
        System.out.println(is); 
    }catch(IOException e){ 
        e.printStackTrace(); 
    } 
} 

反编译后:

try {
    InputStream is = new FileInputStream("d:\\1.txt");
    Throwable var2 = null;

    try {
        System.out.println(is);
    } catch (Throwable var12) {
        var2 = var12;
        throw var12;
    } finally {
        if (is != null) {
            if (var2 != null) {
                try {
                    is.close();
                } catch (Throwable var11) {
                    var2.addSuppressed(var11);
                }
            } else {
                is.close();
            }
        }

    }
} catch (IOException var14) {
    var14.printStackTrace();
}

原创文章,作者:彭晨涛,如若转载,请注明出处:https://www.codetool.top/article/%e6%b7%b1%e5%85%a5%e7%90%86%e8%a7%a3java%e8%99%9a%e6%8b%9f%e6%9c%ba%e7%ac%ac%e4%b8%89%e7%89%88%e8%af%bb%e4%b9%a6%e7%ac%94%e8%ae%b010/

(0)
彭晨涛彭晨涛管理者
上一篇 2020年1月25日
下一篇 2020年1月26日

相关推荐

  • 阻塞队列BlockingQueue详解

    阻塞队列是生产者消费者模式的经典体现。 我们在曾在Java线程池详解中自己实现过一个阻塞队列,这篇文章我们来研究一下JDK中的阻塞队列: BlockingQueue接口主要方法 抛…

    Java 2020年2月14日
    0590
  • Java中的四种内部类

    我发现最近真是越来越没有东西写了。。。不可能天天学习新知识啊,最近在复习阶段了,复习的东西大多数是博客里写过的/(ㄒoㄒ)/ 复习Java基础的时候认真看了一下Java的内部类,这…

    Java 2020年5月23日
    0100
  • Java线程池详解

    线程池就是享元模式和生产者消费者模式的应用 动手实现线程池 步骤1:自定义拒绝策略接口 @FunctionalInterface // 拒绝策略 interface RejectP…

    2020年2月3日
    0310
  • 深入理解java虚拟机第三版读书笔记13

    以下是第十三章 Java内存模型与线程的内容 线程安全 当多个线程同时访问一个对象时,如果不用考虑这些线程在运行时环境下的调度和交替执行,也不需要进行额外的同步,或者在调用方进行任…

    2020年1月30日
    0130
  • 遗留线程安全类Vector和HashTable简要源码分析

    总结 总结放前面防止太长不看 Vector Vector就是使用synchronized限制线程安全的一个List实现。 Vector是基于数组实现的,默认初始容量是10,在构造的…

    Java 2020年2月15日
    0700
  • LinkedList源码分析

    相关文章:ArrayList源码分析 List家族一览: LinkedList简介 LinkedList的超类有List、Queue,说明它既有List的性质也有Queue的性质,…

    2019年12月2日
    080
  • 谈一谈StringBuffer、StringBuilder及String在JDK9中的变化

    本文参考资源: JAVA9 String新特性,说说你不知道的东西Java人在江湖-CSDN博客 StringBuffer和StringBuilder的区别 这里谈的是JDK8中S…

    2020年4月9日
    0350
  • 快速失败(fail-fast)和安全失败(fail-safe)

    快速失败 在用迭代器遍历一个集合对象时,如果遍历过程中对集合对象的内容进行了修改(增加、删除、修改),则会抛出 Concurrent Modification Exception。…

    Java 2020年2月23日
    0150
  • NIO网络编程之Selector介绍

    Selector 要实现异步IO要通过Selector,甚至我们可以通过一个线程管理多个Channel的读写,这是NIO相较BIO的优越之处之一。 Channel可以注册到一个Se…

    Java 2020年2月7日
    0760
  • StringTable性能调优

    StringTable是jvm运行时常量池表中的字符串常量池,深入理解java虚拟机第三版读书笔记02有相关的介绍。 无论是运行时常量表还是StringTable,他们内部都是通过…

    2020年1月10日
    0280

发表回复

登录后才能评论