• Java虚拟机之代码的编译和类加载


    前言

            Java运行在Java虚拟机之上,因此Java语言是跨平台的一门面向对象语言。一处编译,处处运行。是否了解JVM对码出高效代码起到非常关键的作用。对任何一个Java开发者而言。只要是走在大牛的路上,JVM不得不学。

    Java代码如何编译

            如果要在JVM中执行Java代码。首先就需要由Java文件产生class文件,我们把这个过程称为“编译”。这种机制就称为Java源码的编译机制。通过java -version查看我们开发中使用的JDK版本。如下图所示,

    1 java version "1.8.0_171"
    2 Java(TM) SE Runtime Environment (build 1.8.0_171-b11)
    3 Java HotSpot(TM) 64-Bit Server VM (build 25.171-b11, mixed mode)

     第3行:HotSpot(TM)。在HotSpot中Java源码的编译机制就是javac。在理论上来讲Java源码的编译机制总共分为三部分。

    第一部分:分析和输入到符号表。parse 和 enter。

    第二部分:注解处理。Annotation Processing

    第三部分:语义分析和生成class文件。Analyse and  Generate

    ①分析和输入到符号表

    分析包括了语法分析和语义分析。即parse的过程。词法分析将代码字符串转变为token序列;词法分析要根据语法由token序列生成抽象语法树。输出到符号表,即enter的过程。包含了类的超类型和接口等类中出现的符号输入类自身的符号表中。

    ②注解的处理  

    编译器对Java源代码中开发者的自定义注解,用来参数的特殊检查和动态在编译阶段产生代码。这个理论非常重要,动态在编译阶段动态的产生代码,笔者自己早就有想法,构想通过这一项理论在系统级别源码阶段不需要开发者作过多的重复性的工作。想要的效果代码在编译阶段产生。

    ③语义分析和生成class文件

    语义分析这一步主要是基于抽象树来进一步的一系列的语义分析,包含语法树中的名字,表达式等元素与变量,方法,类型联系在一起。同时检查一个变量在使用之前是否已经声明等等。在完成了语义分析之后,开始生成class文件。

    class文件中除了包含字节码信息之外。还包括了结构信息(class文件格式的版本号,以及各个部分的数量与大小信息)、元数据(源代码中的声明和变量)、方法信息(语句和表达式)。

    Class文件如何被JVM识别

    这就是类加载过程。所有的程序都需要加载到内存之后才能与计算机交互。通过编译器编译好的class文件同样需要加载到内存中。这里的内存指的是Java虚拟机内存中。

    这就需要用到加载器,什么加载器?顾名思义就是负责加载class文件,读取数据到内存中的loader。我们把这个loader叫作ClassLoader。类加载器。Java的类加载器是运行时的核心。主要的功能是在启动时,对类,加载,链接,初始化。

    ①加载。读取class文件产生二进制流。并且转化为特定的数据结构。简单校验这个类是否是合法。常量池。文件长度,是否有父类。然后创建对应的类的java.lang.Class实例。

    ②链接。链接阶段分为三个步骤。包含验证,准备,解析。验证则是更加详细的校验,final是不是合规,类型是不是正确。静态变量是不是合理。准备阶段是为静态变量分配内存,并且设定默认值。解析是为类和方法等定位直接引用。完成内存结构布局。

    ③初始化。初始化阶段是执行类构造器的<clinit>方法。

    类加载时讲一个class文件实例化成Class对象。并进行相关初始化的过程。

    如何查看字节码指令

    java源文件被编译器编译为class文件,这个class文件的存储包含了字节码。那么如何来查看这个字节码信息呢?如下代码所示:

     1 package com.example.jvm;
     2 
     3 public class Simple {
     4 
     5 
     6     public int simpleMethod() {
     7         int x = 5;
     8         int y = 7;
     9 
    10         int z = x+y;
    11 
    12         int a = ++x;
    13         int b = y++;
    14 
    15         System.out.println("a="+a);
    16         System.out.println("b="+b);
    17         System.out.println("z="+z);
    18 
    19         return z;
    20     }
    21 
    22 
    23     public static void main(String[] args) {
    24         Simple simple = new Simple();
    25         simple.simpleMethod();
    26     }
    27 }

    我们执行编译的指令,在 HotSpot中就是javac Simple.java。此时在同级目录下新增一个class文件。我们继续执行javap -c Simple。得到的结果如下图所示:

     1 public class com.example.jvm.Simple {
     2   public com.example.jvm.Simple();
     3     Code:
     4        0: aload_0
     5        1: invokespecial #1                  // Method java/lang/Object."<init>":()V
     6        4: return
     7 
     8   public int simpleMethod();
     9     Code:
    10        0: iconst_5
    11        1: istore_1
    12        2: bipush        7
    13        4: istore_2
    14        5: iload_1
    15        6: iload_2
    16        7: iadd
    17        8: istore_3
    18        9: iinc          1, 1
    19       12: iload_1
    20       13: istore        4
    21       15: iload_2
    22       16: iinc          2, 1
    23       19: istore        5
    24       21: getstatic     #2                  // Field java/lang/System.out:Ljava/io/PrintStream;
    25       24: new           #3                  // class java/lang/StringBuilder
    26       27: dup
    27       28: invokespecial #4                  // Method java/lang/StringBuilder."<init>":()V
    28       31: ldc           #5                  // String a=
    29       33: invokevirtual #6                  // Method java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder;
    30       36: iload         4
    31       38: invokevirtual #7                  // Method java/lang/StringBuilder.append:(I)Ljava/lang/StringBuilder;
    32       41: invokevirtual #8                  // Method java/lang/StringBuilder.toString:()Ljava/lang/String;
    33       44: invokevirtual #9                  // Method java/io/PrintStream.println:(Ljava/lang/String;)V
    34       47: getstatic     #2                  // Field java/lang/System.out:Ljava/io/PrintStream;
    35       50: new           #3                  // class java/lang/StringBuilder
    36       53: dup
    37       54: invokespecial #4                  // Method java/lang/StringBuilder."<init>":()V
    38       57: ldc           #10                 // String b=
    39       59: invokevirtual #6                  // Method java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder;
    40       62: iload         5
    41       64: invokevirtual #7                  // Method java/lang/StringBuilder.append:(I)Ljava/lang/StringBuilder;
    42       67: invokevirtual #8                  // Method java/lang/StringBuilder.toString:()Ljava/lang/String;
    43       70: invokevirtual #9                  // Method java/io/PrintStream.println:(Ljava/lang/String;)V
    44       73: getstatic     #2                  // Field java/lang/System.out:Ljava/io/PrintStream;
    45       76: new           #3                  // class java/lang/StringBuilder
    46       79: dup
    47       80: invokespecial #4                  // Method java/lang/StringBuilder."<init>":()V
    48       83: ldc           #11                 // String z=
    49       85: invokevirtual #6                  // Method java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder;
    50       88: iload_3
    51       89: invokevirtual #7                  // Method java/lang/StringBuilder.append:(I)Ljava/lang/StringBuilder;
    52       92: invokevirtual #8                  // Method java/lang/StringBuilder.toString:()Ljava/lang/String;
    53       95: invokevirtual #9                  // Method java/io/PrintStream.println:(Ljava/lang/String;)V
    54       98: iload_3
    55       99: ireturn
    56 
    57   public static void main(java.lang.String[]);
    58     Code:
    59        0: new           #12                 // class com/example/jvm/Simple
    60        3: dup
    61        4: invokespecial #13                 // Method "<init>":()V
    62        7: astore_1
    63        8: aload_1
    64        9: invokevirtual #14                 // Method simpleMethod:()I
    65       12: pop
    66       13: return
    67 }

    第10行:iconst_5代表将常量5压入到操作栈。

    第11行:istore_1代表将常量5保存到局部变量表的slot_1中。

    第12行:bipush 7代表将常量7压入到操作栈。

    第13行:istore_2代表将常量7保存到局部变量表的slot_2中。

    第14行:iload_1代表将局部变量表中的slot_1元素(int x)压入操作栈。

    第15行:iload_2代表将局部变量表中的slot_2元素(int y)压入操作栈。

    第16行:IADD代表将两个数都取出来,计算出来之后,压回到操作栈中的栈顶。

    任何一行代码,在被编译器编译时,都会对应一条或者多条指令。上面只是简单说明,后面会集中式对这些指令的讲解。

  • 相关阅读:
    设计克制
    高性能动画的
    点选日历控件
    adobe工具软件应用
    Visual Studio 2022 性能增强:更快的 C++、优化 Git 分支切换
    省市县的字典表
    Hype 4.0(Mac系统)的布局功能
    RN相关的文章超过100篇高质量文章
    前端格局
    移动端关于平方字体的适配
  • 原文地址:https://www.cnblogs.com/sunshine798798/p/9911282.html
Copyright © 2020-2023  润新知