jvm(10)-早期(编译期)优化

【0】README

0.1)本文部分文字描述转自 “深入理解jvm”,旨在学习  早期(编译期)优化 的基础知识;

0.2)本文部分文字描述转自: http://www.cnblogs.com/zhouyuqin/p/5223180.html

 

【1】概述

1)java中的编译期是一段不确定的操作过程(process),可能是:
  • process1)指一个前端编译器把 *.java 文件转变为 *.class 文件的过程;
  • process2)指虚拟机的后端运行期编译器(JIT编译器,just in time compiler)把字节码转变为机器码的过程;
  • process3)指使用静态提前编译器(AOT编译器,Ahead Of Time Compiler)直接把*.java  文件编译成本地机器码的过程;
2)下面是编译过程中有代表性的编译器:
  • 2.1)前端编译器:Sun 的javac,Eclipse JDT 中的增量式编译器(ECJ);
  • 2.2)JIT编译器:HotSpot VM 的C1, C2 编译器;
  • 2.3)AOT编译器: GNU Compiler for the Java(GCJ),Excelsior JET;
Attention)本文提到的编译器是前端编译器;

【2】 Javac 编译器
【2.1】 Javac 的源码和调试
1)从 Sun Javac 的代码来看,编译过程大致可以分为3个过程,分别是:(干货——编译过程大致分为3个过程)
  • step1)解析和填充符号表过程;
  • step2)插入式注解处理器的注解处理过程;
  • step3)分析与字节码生成过程;
2)以上3个步骤之间的关系与交互顺序如下图所示:

2.1)Javac 编译动作的入口: 是 com.sun.tools.javac.main.JavaCompiler 类,上述3个过程的代码逻辑集中在这个类的 compile() 和 compile2() 方法中,主体代码如下图所示,整个编译最关键的处理就由以下8个方法来完成:

【2.2】解析与填充符号表
1)解析符号表的步骤(词法、语法分析+填充符号表)
1.1)词法、语法分析(干货——引入词法、语法分析)
  • 1.1.1)词法分析:是将源代码的字符流转变为 标记集合,单个字符是程序编写过程的最小元素,而标记则是编译过程的最小元素,关键字、变量名、字面量、运算符都可以成为标记;如“int a = b+2” 包含了6个标记,分别是 int, a, =, b, +, 2 ;
  • 1.1.2)语法分析:是根据Token 序列构造抽象语法树的过程,抽象语法树(abstract syntax tree==AST)是一种用来描述程序代码语法结构的树形表示方式,语法树的每一个节点都代表着程序代码中的一个语法结构,如包,类型,修饰符,运算符,接口,返回值等;
1.2)填充符号表
  • 1.2.1)符号表定义:符号表是一组符号地址和符号信息构成的表格,读者可以把它想象成哈希表中键值对的形式;
  • 1.2.2)完成抽象语法树之后,下一步就是填充符号表的过程,即enterTrees()方法。符号表是由一组符号地址和符号信息构成的表格,类似于哈希表中K-V值对的形式。符号表中所登记的信息在编译的不同阶段都要用到。当对符号名进行地址分配时,符号表是地址分配的依据。填充过程由com.sun.tools.javac.comp.Enter类实现。
【2.3】注解处理器

1)JDK1.5之后,Java提供了对注解的支持,这些注解与普通的Java代码一样,在运行期间发挥作用。 有了编译器注解处理的标准API后,我们的代码才有可能干涉编译器的行为,由于语法树中的任意元素,甚至包括代码注释都可以在插件之中访问到,所以使用插入式注解处理器在功能上有很大的发挥空间。

【2.4】语义分析与字节码生成

0)语法分析之后,编译器获得了程序代码的抽象语法树表示,语法树能表示一个结构正确的源代码抽象。而语义分析的主要任务是对结构上正确的源程序进行上下文有关性质的审查,如进行类型审查。在Javac编译过程中,语法分析过程分为标注检查以及数据及控制流分析两个步骤,分别对应着attribute()和flow()方法完成。

1)标注检查 :标注检查步骤检查的内容包括诸如:变量使用前是否已被声明、变量与赋值之间的数据类型是否能够匹配等。此外,这个过程中还有一个重要的步骤称为常量折叠。 

标注检查步骤在Javac源码中的实现类是com.xun.tools.javac.comp.Attr和com.sun.tools.javac.comp.Check类。
2)数据及控制流分析 :数据及控制流分析是对程序上下文逻辑更进一步的验证,它可以查出诸如程序员局部变量在使用前是否有赋值、方法的每条路径是否都有返回值、是否所有的受查异常都被正确处理了等问题。编译期的数据及控制流分析与类加载时的数据及数据流分析的目的基本上是一致的,但校验范围有所区别,有一些校验项只有在编译期或者运行期才能进行。如将局部变量声明为final,对运行期是没有影响的,变量的不变性仅仅由编译器在编译期间保障,在Javac的源码中,数据及控制流分析的入口是flow()方法,具体操作由com.sun.tools.javac.comp.Flow类来完成。(干货——将局部变量声明为final,对运行期是没有影响的,变量的不变性仅仅由编译器在编译期间保障, 看个荔枝如下)

3)解语法糖 (干货——语法糖就是一种编写代码的语法)
  • 3.1)语法糖:是指在计算机语言中添加某种语法,这种语法对语言的功能并没有影响,但是更方便程序员使用。 
  • 3.2)Java是一种“低糖语言”:常用的语法糖主要是之前提到的泛型、变长参数、自动装箱/拆箱等。虚拟机运行时不支持这些语法,它们在编译期还原回简单的基础语法结构,这个过程称为解语法糖。解语法糖的过程是由desuger()方法触发的。(干货——引入解语法糖的概念)
4)字节码生成 :
  • 4.1)字节码生成是Javac编译过程的最后一个阶段:由com.sun.tools.javac.jvm.Gen类来完成,字节码生成阶段不仅仅是把前面各个步骤所生成的信息(语法树、符号表)转化为字节码写入磁盘中,编译器还进行了少量代码添加和转换工作。 
  • 4.2)完成对语法树的遍历与调整之后:就会把填充了所有所需信息的符号表交给com.sun.tools.javac.jvm.ClassWriter类,由这个类的wrtieClass()方法输出字节码,生成最终的Class文件。
【3】 java语法糖
【3.1】泛型和类型擦除
1)泛型是JDK1.5新增的特性,它的本质是参数化类型的应用,也就是说所操作的数据类型被指定为一个参数,这种参数类型可以用于类、接口和方法的创建中,分别称为泛型类、泛型接口和泛型方法。 

2)与C#的泛型不一样的是:Java的泛型只存在于程序源码中,在编译后的字节码文件中,就已经替换成原来的原生类型,也称为裸类型,并且在相应的地方插入了强制转型代码。因此,对于运行期的Java语言来说,ArrayList<int>与ArrayList<String>就是同一个类,所以泛型技术实际上是Java语言的一颗语法糖,Java语言中的泛型实现方法称为类型擦除,基于这种方法实现的泛型称为伪泛型。 (干货——对于运行期的Java语言来说,ArrayList<int>与ArrayList<String>就是同一个类)

2.1)看个荔枝

public class GenericEraseTest {public static void main1(String[] args) { // 泛型擦除前的荔枝Map<String, String> map = new HashMap<>();map.put("hello", "你好");map.put("how are you?", "吃了没?");System.out.println(map.get("hello"));System.out.println(map.get("how are you?"));}public static void main(String[] args) { // 泛型擦除后的荔枝Map map = new HashMap();map.put("hello", "你好");map.put("how are you?", "吃了没?");System.out.println(map.get("hello"));System.out.println(map.get("how are you?"));}
}

3)故当List<Integer>和List<String>作为参数时,擦除使得两者的特征签名变得一模一样,有时可能导致拥有该两个方法参数的方法无法重载。


Attention)值得注意的是:当出现上述的情况的时候,如果返回值不一样的话,该两个方法是可以存在于一个Class文件中的,总结一下,两个方法如果有相同的名称和特征签名,但返回值不同,那它们也是合法地,可以共存于一个Class文件中。

4)擦除法所谓的擦除:仅仅是对方的Code属性中的字节码进行擦除,实际上元数据中还是保留了泛型信息,这也是我们能通过反射手段取得参数化类型的根本依据。(干货——擦除法的执行本质)

【3.2】自动装箱、拆箱与遍历循环

1)自动装箱、自动拆箱与遍历循环这些语法糖:无论实现上还是思想上都不能和上文介绍的泛型相比,两者的难度和深度都有很大差距;但是它们是java 中使用得最多的语法糖;

1.1)看个荔枝:

public class SyntacticSugar {// 自动装箱,拆箱与遍历循环public static void main1(String[] args) {List<Integer> list = Arrays.asList(1, 2, 3, 4);// 如果在jdk1.7中,还有另外一颗语法糖 // 能让上面这句代码进一步简写成 List<Integer> list = [1, 2, 3, 4];int sum = 0;for (int i : list) {sum += i;}System.out.println(sum);}// 自动装箱,拆箱与遍历循环编译之后public static void main(String[] args) {List list = Arrays.asList(new Integer[] {Integer.valueOf(1),Integer.valueOf(2),Integer.valueOf(3),Integer.valueOf(4),Integer.valueOf(5)});int sum = 0;}
}

1.2)再看个荔枝,自动装箱的陷阱

public class AutoPackage
{public static void main(String[] args){Integer a = 1;Integer b = 2;Integer c = 3;Integer d = 3;Integer e = 312;Integer f = 312;Long g = 3L;System.out.println(c == d); // trueSystem.out.println(e == f); // falseSystem.out.println(c == (a+b)); // trueSystem.out.println(c.equals(a+b)); // trueSystem.out.println(g == (a+b)); // trueSystem.out.println(g.equals(a+b)); // false}
}

对以上代码的分析(Analysis):鉴于包装类的 == 运算在不遇到算术运算的情况下不会自动拆箱,以及它们equals()方法不处理数据转型的关系,作者建议在实际编码中应该尽量避免这样使用自动装箱和拆箱;

【3.3】条件编译 

1)java语言也可以进行条件编译,方法就是使用条件为常量的if语句;

// 通过if语句实现条件编译
public class ContitionalCompile {public static void main(String[] args) {if(true) {System.out.println("true");} else {System.out.println("false");}}
}

2)Java语言中条件编译的实现,也是Java语言的一颗语法糖:根据布尔常量值的真假,编译器将会把分支中不成立的代码块消除掉,这是在解语法糖阶段实现的。

3)Java语言中还有不少的其他语言糖:如内部类、枚举类、断言语句、对枚举和字符串的switch支持、try语句中定义和关闭资源等等。


本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/331285.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

etl介绍与etl工具比较_ETL万岁

etl介绍与etl工具比较提取转换负载是从一个数据系统中提取数据并加载到另一个数据系统中的过程。 涉及的数据系统称为源系统和目标系统。 来自源系统的数据形状与目标系统不匹配&#xff0c;因此需要进行一些转换以使其兼容&#xff0c;该过程称为Transformation 。 转换是由m…

漫画:高并发下的HashMap

转载自 玻璃猫 程序员小灰上一期我们介绍了HashMap的基本原理&#xff0c; 这一期我们来讲解高并发环境下&#xff0c;HashMap可能出现的致命问题。HashMap的容量是有限的。当经过多次元素插入&#xff0c;使得HashMap达到一定饱和度时&#xff0c;Key映射位置发生冲突的几率会…

jvm(11)-晚期(运行期)优化

【0】README 0.1&#xff09;本文部分文字描述转自 “深入理解 jvm”&#xff0c;旨在学习 晚期&#xff08;运行期&#xff09;优化 的基础知识&#xff1b; 【1】概述 1&#xff09;即时编译器&#xff08;JITjust in time compiler&#xff09;定义&#xff1a;为了提高…

java 新功能_Java 14的新功能

java 新功能2020年3月17日&#xff0c;Oracle发布了名为Java 14的Java新版本&#xff0c;其中包括许多新功能&#xff0c;工具&#xff0c;安全性&#xff0c;调试和更新的文档方面的改进。 但是&#xff0c;Oracle还向您提供Java的较旧版本&#xff0c;因为它具有向后兼容性&a…

漫画:什么是ConcurrentHashMap

转载自 玻璃猫 程序员小灰————————————————————————前两期我们讲解了HashMap的基本原理&#xff0c;以及高并发场景下存在的问题。没看过的小伙伴可以点击下面链接&#xff1a;漫画&#xff1a;什么是HashMap&#xff1f; 漫画&#xff1a;高并发下的H…

jvm(12)-java内存模型与线程

【0】README0.1&#xff09;本文部分文字描述转自“深入理解jvm”&#xff0c;旨在学习“java内存模型与线程” 的基础知识&#xff1b;【1】概述1&#xff09;并发处理的广泛应用是使得 Amdahl 定律代替摩尔定律称为计算机性能发展源动力的根本原因&#xff1b;2&#xff09;A…

junit mockito_从工作中清除代码–使用JUnit 5,Mockito和AssertJ编写可执行规范

junit mockito可执行规范是也可以用作设计规范的测试。 通过启用通用语言&#xff08;在DDD世界中&#xff0c;这也称为无处不在的语言 &#xff09;&#xff0c;它们使技术和业务团队能够进入同一页面。 它们充当代码的未来维护者的文档。 在本文中&#xff0c;我们将看到一种…

漫画:什么是红黑树

转载自 玻璃猫 程序员小灰————————————二叉查找树&#xff08;BST&#xff09;具备什么特性呢&#xff1f;1.左子树上所有结点的值均小于或等于它的根结点的值。 2.右子树上所有结点的值均大于或等于它的根结点的值。 3.左、右子树也分别为二叉排序树。下图中这棵树…

jvm(13)-线程安全与锁优化

【0】README 0.1&#xff09;本文部分文字转自“深入理解jvm”&#xff0c; 旨在学习 线程安全与锁优化 的基础知识&#xff1b; 0.2&#xff09;本文知识对于理解 java并发编程非常有用&#xff0c;个人觉得&#xff0c;所以我总结的很详细&#xff1b; 【1】概述 【2】线程安…

apache ignite_Kubernetes集群上的Apache Ignite和Spring第3部分:测试应用程序

apache ignite在上一个博客中&#xff0c;我们为Ignite应用程序创建了Kubernetes部署文件。 在此博客上&#xff0c;我们将在Kubernetes上部署Ignite应用程序。 我将在此使用minikube。 让我们先构建 mvn clean install 我将创建一个简单的Docker映像&#xff0c;因此需要Doc…

什么是AES算法?(整合版)

转载自 玻璃猫 程序员小灰 假设有一个发送方在向接收方发送消息。如果没有任何加密算法&#xff0c;接收方发送的是一个明文消息&#xff1a;“我是小灰” 如果消息被中间人截获到&#xff0c;即使中间人无法篡改消息&#xff0c;也可以窥探到消息的内容&#xff0c;从而暴露了…

soapui 测试soap_使用SoapUI调用不同的安全WCF SOAP服务-基本身份验证,第二部分

soapui 测试soap在本系列的第一篇文章中&#xff0c;我们创建了一个基本的身份验证服务&#xff0c;以使用SoapUI进行调用。 因此&#xff0c;在第二篇文章中&#xff0c;我们将逐步演示如何使用此工具成功调用这种服务。 使用SoapUI的1-Basic WCF SOAP –创建新的SOAP项目 首…

如何获得即时编译器(JIT)的汇编代码(linux环境下)

【0】README0.1&#xff09;本文主要解决如何在linux下获取即时编译器的汇编代码问题&#xff1b;0.2&#xff09;本文部分内容转自&#xff1a;http://psy-lob-saw.blogspot.jp/2013/01/java-print-assembly.html1&#xff09;给定java源代码// 单例模式&#xff08;分析volat…

漫画:什么是SHA系列算法

转载自 玻璃猫 程序员小灰 SHA-1 SHA-1算法可以从明文生成160bit的信息摘要&#xff0c;示例如下&#xff1a; 给定明文&#xff1a;abcd SHA-1摘要&#xff1a;81FE8BFE87576C3ECB22426F8E57847382917ACF SHA-1 与 MD5的主要区别是什么呢&#xff1f; 1.摘要长度不同。 …

apache ignite_Kubernetes集群上的Apache Ignite和Spring第1部分:Spring Boot应用程序

apache ignite在之前的一系列博客中&#xff0c;我们在Kubernetes集群上启动了一个Ignite集群。 在本教程中&#xff0c;我们将使用先前在Spring Boot Application上创建的Ignite集群。 让我们使用Spring Boot创建我们的项目。 Spring Boot应用程序将连接到Ignite集群。 让我…

tomcat(1)一个简单的web server

【0】README 0.1&#xff09;本文部分描述转自“深入剖析tomcat”&#xff0c; 旨在学习 一个简单的web server 的基础知识&#xff1b; 0.2&#xff09;for complete source code, please visit https://github.com/pacosonTang/HowTomcatWorks/tree/master/chapter1 【1】…

漫画:什么是MD5算法

转载自 玻璃猫 程序员小灰 摘要哈希生成的正确姿势是什么样呢&#xff1f;分三步&#xff1a; 1.收集相关业务参数&#xff0c;在这里是金额和目标账户。当然&#xff0c;实际应用中的参数肯定比这多得多&#xff0c;这里只是做了简化。 2.按照规则&#xff0c;把参数名和参数…

idea快速生成crud_Java / Spring:如何快速生成完整的Swagger文档CRUD REST API

idea快速生成crud作为开发人员&#xff0c;我们在日常生活中经常面临的最繁琐的任务之一就是编写良好且易于理解的文档。 无论我们的文档只有几行来解释功能的核心功能&#xff0c;还是表明系统的来龙去脉的成熟文章都没关系。 重要的是&#xff0c;我们试图通过文档传达的信息…

漫画:如何破解MD5算法

转载自 玻璃猫 程序员小灰 在之前的漫画中&#xff0c;我们介绍了MD5算法的基本概念和底层原理&#xff0c;没看过的小伙伴们可以点击下面的链接&#xff1a;《漫画&#xff1a;什么是MD5算法&#xff1f;》 这一次&#xff0c;我们来讲解如何破解MD5算法。 设MD5的哈希函数是…

自定义类加载器(ClassLoader + URLClassLoader)

【0】README 0.1&#xff09;本文主要对类加载器进行分析&#xff0c;且 URLClassLoader是 ClassLoader的子类&#xff1b; 0.2&#xff09;关于如何设置类加载器的加载路径&#xff0c;参见 对servlet容器的补充 【1】URLClassLoader类加载器 1.1&#xff09;URLClassLoad…