jvm是java虚拟机吧,深入理解java虚拟机

  jvm是java虚拟机吧,深入理解java虚拟机

  如何解决写爬虫IP受阻的问题?立即使用。

  以这张图为例。从。java到。类是一个编译过程,从。类到机器代码是一个解释过程。以下分别优化。在优化过程中,编译阶段的优化主要是前端编译器的优化,运行阶段的优化主要是即时编译器的优化。

  编译器优化

  编译过程

  以上是javac的编译流程图,以下是javac编译流程的主要代码。

  下面详细解释了这些步骤。

  1.分析并填写符号表。

  词汇分析

  把源代码的字符流变成一组Token,Token是编译过程中最小的元素,比如a,=,b,int。

  语法分析

  根据标记序列构建抽象语法树。以后编译器基本不会对源文件进行操作,后续操作都是基于抽象语法树。抽象语法树是一种用来描述程序代码语法结构的树表示。节点表示代码中的语法结构,如修饰符、返回值等。

  填充符号表

  符号表是由一组符号地址和符号信息组成的表,用于编译的不同阶段。比如在语义分析中,用于语义检查,生成中间代码;在目标代码生成阶段,它被用作地址分配的基础。

  2、注解处理器

  其中一部分是插件注释处理器在编译期间处理注释的过程。它可以修改语法树。一旦它被修改,编译器将返回到上面的第一步进行重新处理。每个循环称为一轮,就是上图中的循环过程。

  3、语义分析与字节码生成

  经过语法分析,生成的语法树是源程序的抽象,结构正确,但不能保证源程序是逻辑的。语义分析的任务是检查结构正确的源程序的上下文敏感性质。例如,下面代码中的错误只能在语义分析阶段检测到。

  布尔a=假;

  char b=2;

  Int c=a b这个阶段包括以下四个步骤:

  标注检查

  变量在使用前是否已经声明,变量和赋值之间的数据类型是否可以匹配等。还有一种是常数折叠,就是把a=1 2换成a=3。因此,代码中的a=1 2和a=3不会增加程序运行时cpu指令的计算量。

  数据及控制流分析

  检查程序的局部变量在使用前是否赋值,方法的每个路径是否有返回值,所有检查到的异常是否处理正确。在类加载期间还有一个数据和控制流分析。其目的基本相同,只是验证的范围不同。有些验证项只能在编译时或运行时运行。

  解语法糖

  语法是在计算机语言中加入一些语法,对语言的功能没有影响,但可以提高程序的可读性。语法包括泛型、自动解包等。虚拟机在运行时不支持这些语法,它们在编译时被还原为基本语法结构。这个过程叫做解析糖。

  字节码生成

  将前面步骤生成的信息(语法树,符号表)转换成字节码写入磁盘,然后添加并转换少量代码。例如,用StringBuffer或StringBuilder的append()操作替换string的加法操作。

  此时,生成了类文件。

  语法糖

  Sugar是java中加入的一种语法,对语言的功能没有影响,但是可以增加程序的可读性。包括泛型、内部类、枚举类等。

  1.泛型和类型擦除

  泛型可用于创建类、接口和方法,并用于约束放置在集合中的元素的类型。泛型只存在于程序的源代码中,在编译阶段就有解决语法糖的步骤,所以在。类文件。这个过程被称为类型擦除。

  通用擦除前:

  公共静态void main(String[] args){

  MapString,String map=new HashMap();

  Map.put (name ,晓明);

  Map.put(性别,男性);

  sout(map . get( name ));

  Sout(map.get(性别));

  }清除泛型后:

  公共静态void main(String[] args){

  map map=new HashMap();

  Map.put (name ,晓明);

  Map.put(性别,男性);

  Sout(字符串)地图。

  sout((String)map . get( gender ));

  }所以ArrayList和ArrayList在运行时是同一个类。

  2.盒子的自动拆装和循环遍历。

  这些是java中使用最多的语法糖。编译前:

  公共静态void main(String[] args){

  list integer list=arrays . as list(1,2,3,4);

  int sum=0;

  for(int i:list){

  sum=I;

  }

  system . out . println(sum);

  }编译后:

  公共静态void main(String[] args){

  list list=arrays . aslist(new Integer[]{

  Integer.valueOf(1),

  Integer.valueOf(2),

  Integer.valueOf(3),

  integer . value of(4)});

  int sum=0;

  for(Iterator local Iterator=list . Iterator();local iterator . has next();){

  int I=((Integer)local iterator . next())。int value();

  sum=I;

  }

  system . out . println(sum);

  }可以看到,自动反汇编框在编译后被转换成相应的打包和还原方法,比如Integer.valueOf()和Integer.intValue()。

  循环将代码还原到迭代器的实现。

  3.条件编译

  根据真、假布尔常数值,编译器会剔除分支中的无效代码块。

  公共静态void main(String[] args){

  如果(真){

  sout(“第一街区”);

  }否则{

  sout(“第二街区”);

  }

  }编译后,代码变成:

  公共静态void main(String[] args){

  sout(“第一街区”);

  }运行期优化

  一般我们编译。java成。类,然后解释。类转换成机器码。但也有特殊情况。有些代码调用频繁,比如某个方法或者代码块运行非常频繁。为了提高程序的执行效率,虚拟机在运行时直接将这段代码编译成机器码,并在各个层面进行优化。这种代码被称为热点代码。完成这项任务的编译器被称为实时编译器。但它不是虚拟机的必要部分。

  实时编译器概述

  (1)为什么虚拟机要使用解释器和编译器并存的架构?

  

  虚拟机包含解释器和编译器。当程序需要快速启动和执行时,解释器可以先发挥作用,节省编译和立即执行的时间。程序运行后,随着时间的推移,编译器逐渐发挥作用,将越来越多的代码编译成本地代码后,可以达到更高的执行效率。

  当程序运行环境中的内存资源有限时,我们可以使用解释来节省内存,反之,我们可以使用编译来提高效率。

  (2)为什么虚拟机要实现两个不同的即时编译器?

  虚拟机内置了两个实时编译器,即客户端编译器和服务器编译器,也称为C1和C2。

  默认情况下只使用其中一个。至于选择哪一个,要看虚拟机根据自身版本和主机硬件性能自动选择运行模式。用户也可以使用"-客户端"和"-服务器"来指定。

   (3)程序何时使用解释器执行?何时使用编译器执行?

  虚拟机具有分层编译策略。

  0级:解释并执行程序,解释程序可以触发1级编译,而无需启动性能监控功能。

  第1层:也称为C1编译,它将字节码编译成本地代码,进行简单可靠的优化,如果需要,还可以添加性能监控的逻辑。

  第二层:也称为C2编译,它将字节码编译成本地代码,但会启用一些需要很长时间编译的优化,甚至基于性能监控信息做出一些不可靠的激进优化。

  (4)哪些程序代码会被编译为本地代码?如何编译为本地代码?

  热代码包括以下两类,都是以整个方法为编译对象。

  一个被多次调用的方法

  b,多次执行的循环体

  热点检测用于判断一段代码是否是热点代码,有两种方式:

  一、基于抽样调查

  b、基于计数器。热点用这个。它为每个方法准备了两种计数器:方法调用计数器,计算方法被调用的次数;反向计数器,计算循环代码在方法中被执行的次数。

  (5)如何从外部观察及时编译器的编译过程和编译结果?

  可以使用-xx: PrintCompilation来查看即时编译器编译了哪些方法。

  优化技术有哪些?

  当虚拟机的实时编译器生成代码时,它采用以下代码优化技术。

  (1)公共子表达式消除

  如果一个表达式E已经计算过了,那么如果E再次出现就不会再计算了。例如:

  Int=(a * b) * 12c (CB * a)如果这段代码交给javac编译器,不会进行任何优化。如果将它提供给实时编译器,它将通过以下步骤进行优化:

  第一步:消除常见的子表达式。

  Int=e * 12c (c e)第二步:代数化简:

  int d=E*13 c*2(2)数组边界检查消除

  什么是数组边界检查?

  如果有一个数组foo[],在java语言中访问数组元素foo[i]时,系统会自动检查上下界的范围,检查I是否满足条件0 i 0ifoo.length

  那怎么消除呢?

  一、把运行时检查提到编译时完成。比如foo[3],只要编译时根据数据流分析确定foo.length的值,并且下标“3”没有越界,执行时就不需要判断。

  b、隐式异常处理。这种思想通常用于除数为零时的空指针检查和运算符运算。

  如果(foo!=null){

  返回foo.value

  }否则{

  抛出新的NullPointException();

  }经过隐式异常处理优化后,变成如下代码:

  尝试{

  返回foo.value

  }catch(segment_fault){

  uncommon _ trap();

  }除了数组边界检查消除,还有自动装箱消除、安全点消除、反射消除等。

  (3)方法内联

  将目标方法的代码“复制”到调用方法中,以避免实际的方法调用。

  public int add(int x1,int x2,int x3,int x4) {

  返回add1(x1,x2) add1(x3,x4);

  }

  public int add1(int x1,int x2) {

  返回x1 x2

  }运行一段时间后,JVM会移除add1方法,将代码翻译成:

  public int add(int x1,int x2,int x3,int x4) {

  返回x1 x2 x3 x4

  }(4)逃逸分析

  当一个对象定义在一个方法中时,它可能被外部方法引用,比如作为调用参数传递给其他方法,这叫做方法转义。类似地,如果被外部线程访问,则称为线程转义。

  变量的对应分析称为逃逸分析。如果可以证明其他方法或线程无法以任何方式访问这个对象,可以对这个变量进行一些优化。

  优化方法包括栈上分配、同步消除、标量替换等。以同步消除为例。如果转义分析可以确定某个变量不会从线程中转义,即不能被其他线程访问,那么就可以消除对这个变量实现的同步措施。

  以上内容都是关于JAVA虚拟机中的JVM优化。更多相关问题请访问PHP中文网:JAVA视频教程。以上是JAVA虚拟机(JVM)的详细介绍(七)。——JVM优化的细节。请多关注我们的其他相关文章!

郑重声明:本文由网友发布,不代表盛行IT的观点,版权归原作者所有,仅为传播更多信息之目的,如有侵权请联系,我们将第一时间修改或删除,多谢。

留言与评论(共有 条评论)
   
验证码: