JVM的基础学习到扎实

                      JVM的基础学习到扎实--class文件详解

Java工作几年之后,特别是从基础到工作一年以上,还有的是培训班出来的,自学的,写了大量的代码,但是,这些代码是怎么工作的,怎么执行的,怎么变成我们需要的结果?对于没有接触过JVM的同学来说是一个问题。也有的不知道JVM是干什么用的,怎么来的。这是问题根本。

作为Java开发者,我们都知道,自己在eclipse写的代码,执行后就看到结果,但是执行过程中是怎么样的,需要我们去搞懂。

1.JVM干什么用的?

JVM就是虚拟机,专门是用来解析写Java的字节码,最后变成我们自己想要的结果。如下图所示:

JVM的基础学习到扎实_第1张图片

 2.jdk,jre,jvm三者之间的关系

JVM的基础学习到扎实_第2张图片

JDK有两种,分别为OracleJDK和OpenJDK

查看JDK的版本:

进去终端里面输入命令查看:java -version;

java -version;

显示结果:

java version "1.8.0_131"
Java(TM) SE Runtime Environment (build 1.8.0_131-b11)
Java HotSpot(TM) 64-Bit Server VM (build 25.131-b11, mixed mode)
192:~ mac1094$ 

以上就是OracleJDK。

如果是OpenJDK,显示信息:

openjdk version "1.8.0_144"
OpenJDK Runtime Environment (build 1.8.0_144-b01)
OpenJDK 64-Bit Server VM (build 25.144-b01, mixed mode)

JVM的运行模式有两种:Server模式和Client模式

两种模式的区别在于:

Client模式启动速度较快,Server模式启动较慢;

但是启动进入稳定期长期运行之后Server模式的程序运行速度比Client要快很多。

因为Server模式启动的JVM采用的是重量级的虚拟机,对程序采用了更多的优化;而Client模式启 动的JVM采用的是轻量级的虚拟机。所以Server启动慢,但稳定后速度比Client远远要快。

3.JVM的架构图(很重要)

JVM的基础学习到扎实_第3张图片

 

根据架构图中可以看出,JVM架构由三大板块构成,首先要记住三大板块的逻辑结构关系,然后再记住三大板块里的功能,切记死记硬背,理解的基础上记忆,每天画一下架构图,坚持一周就可以记得牢固。

JVM的执行流程:

JVM的基础学习到扎实_第4张图片

4.class文件是怎么加载的?(重在理解)

我们随便写一个Java源代码,打开他的Class文件(使用Hex Editor等其他工具打开),如下图所示:

JVM的基础学习到扎实_第5张图片

class文件的结构很复杂,需要理解:如下图:

JVM的基础学习到扎实_第6张图片

魔数:class文件最初的4个字节都是以"0xCAFEBABE"来的,判断的唯一标准。

版本号:分为主版本号和副版本号,各占2字节。

minor_version( JDK次版本号),
major_version( JDK主版本号)

可以查看JVM编译的代码,使用命令工具:javap -v class文件名

192:myapp mac1094$ javap -v Math.class

显示结果:

Classfile /Users/mac1094/Desktop/myapp/Math.class
  Last modified May 17, 2019; size 401 bytes
  MD5 checksum 1b66de1534877806cd9a6747dbfc330a
  Compiled from "Math.java"
public class Math
  minor version: 0
  major version: 52
  flags: ACC_PUBLIC, ACC_SUPER
Constant pool:
   #1 = Methodref          #5.#14         // java/lang/Object."":()V
   #2 = Fieldref           #15.#16        // java/lang/System.out:Ljava/io/PrintStream;
   #3 = Methodref          #17.#18        // java/io/PrintStream.println:(I)V
   #4 = Class              #19            // Math
   #5 = Class              #20            // java/lang/Object
   #6 = Utf8               
   #7 = Utf8               ()V
   #8 = Utf8               Code
   #9 = Utf8               LineNumberTable
  #10 = Utf8               main
  #11 = Utf8               ([Ljava/lang/String;)V
  #12 = Utf8               SourceFile
  #13 = Utf8               Math.java
  #14 = NameAndType        #6:#7          // "":()V
  #15 = Class              #21            // java/lang/System
  #16 = NameAndType        #22:#23        // out:Ljava/io/PrintStream;
  #17 = Class              #24            // java/io/PrintStream
  #18 = NameAndType        #25:#26        // println:(I)V
  #19 = Utf8               Math
  #20 = Utf8               java/lang/Object
  #21 = Utf8               java/lang/System
  #22 = Utf8               out
  #23 = Utf8               Ljava/io/PrintStream;
  #24 = Utf8               java/io/PrintStream
  #25 = Utf8               println
  #26 = Utf8               (I)V
{
  public Math();
    descriptor: ()V
    flags: ACC_PUBLIC
    Code:
      stack=1, locals=1, args_size=1
         0: aload_0
         1: invokespecial #1                  // Method java/lang/Object."":()V
         4: return
      LineNumberTable:
        line 1: 0

  public static void main(java.lang.String[]);
    descriptor: ([Ljava/lang/String;)V
    flags: ACC_PUBLIC, ACC_STATIC
    Code:
      stack=2, locals=4, args_size=1
         0: iconst_1
         1: istore_1
         2: iconst_2
         3: istore_2
         4: iload_1
         5: iload_2
         6: iadd
         7: bipush        10
         9: imul
        10: istore_3
        11: getstatic     #2                  // Field java/lang/System.out:Ljava/io/PrintStream;
        14: iload_3
        15: invokevirtual #3                  // Method java/io/PrintStream.println:(I)V
        18: return
      LineNumberTable:
        line 3: 0
        line 4: 2
        line 5: 4
        line 6: 11
        line 7: 18
}
SourceFile: "Math.java"

常量池计数器:

常量池是class文件中非常重要的结构,它描述着整个class文件的字面量信息。常量池是由一组 constant_pool结构体数组组成的,而数组的大小则由常量池计数器指定。常量池计数器 constant_pool_count 的值 =constant_pool表中的成员数+ 1。constant_pool表的索引值只有在大于 0 且小于constant_pool_count时才会被认为是有效的。

常量池数据区:

字面量和符号引用

访问标志:

访问标志,access_flags 是一种掩码标志,用于表示某个类或者接口的访问权限及基础属性。

类索引:

类索引,this_class的值必须是对constant_pool表中项目的一个有效索引值。constant_pool表 在这个索引处的项必须为CONSTANT_Class_info 类型常量,表示这个 Class 文件所定义的类或接 口。

父类索引:

父类索引,对于类来说,super_class 的值必须为 0 或者是对constant_pool 表中项目的一个有 效索引值。

如果它的值不为 0,那 constant_pool 表在这个索引处的项必须为CONSTANT_Class_info 类型常 量,表示这个 Class 文件所定义的类的直接父类。当前类的直接父类,以及它所有间接父类的 access_flag 中都不能带有ACC_FINAL 标记。对于接口来说,它的Class文件的super_class项的 值必须是对constant_pool表中项目的一个有效索引值。constant_pool表在这个索引处的项必须为 代表 java.lang.Object 的 CONSTANT_Class_info 类型常量 。

如果 Class 文件的 super_class的值为 0,那这个Class文件只可能是定义的是 java.lang.Object类,只有它是唯一没有父类的类。

接口计数器:

 接口计数器,interfaces_count的值表示当前类或接口的【直接父接口数量】。

接口信息数据区:

接口表,interfaces[]数组中的每个成员的值必须是一个对constant_pool表中项目的一个有效索引 值, 它的长度为 interfaces_count。每个成员interfaces[i] 必须为 CONSTANT_Class_info类型常量,其中 【0 ≤ i

字段计数器:

字段计数器,fields_count的值表示当前 Class 文件 fields[]数组的成员个数。 fields[]数组 中每一项都是一个field_info结构的数据项,它用于表示该类或接口声明的【类字段】或者【实例字 段】。

字段信息数据区:

字段表,fields[]数组中的每个成员都必须是一个fields_info结构的数据项,用于表示当前类或接 口中某个字段的完整描述。 fields[]数组描述当前类或接口声明的所有字段,但不包括从父类或父接 口继承的部分。

方法计数器:

方法计数器, methods_count的值表示当前Class 文件 methods[]数组的成员个数。Methods[] 数组中每一项都是一个 method_info 结构的数据项。

方法信息数据区:

方法表,methods[] 数组中的每个成员都必须是一个 method_info 结构的数据项,用于表示当前类 或接口中某个方法的完整描述。

如果某个method_info 结构的access_flags 项既没有设置 ACC_NATIVE 标志也没有设置 ACC_ABSTRACT 标志,那么它所对应的方法体就应当可以被 Java 虚拟机直接从当前类加载,而不需 要引用其它类。

method_info结构可以表示类和接口中定义的所有方法,包括【实例方法】、【类方法】、【实例初始 化方法】和【类或接口初始化方法】。

methods[]数组只描述【当前类或接口中声明的方法】,【不包括从父类或父接口继承的方法】。

属性计数器:

属性计数器,attributes_count的值表示当前 Class 文件attributes表的成员个数。 attributes表中每一项都是一个attribute_info 结构的数据项。

属性信息数据区:

属性表,attributes 表的每个项的值必须是attribute_info结构。

在Java 7 规范里,Class文件结构中的attributes表的项包括下列定义的属性: InnerClasses
、 EnclosingMethod 、 Synthetic 、Signature、SourceFile,SourceDebugExtension 、Deprecated、RuntimeVisibleAnnotations 、RuntimeInvisibleAnnotations以及 BootstrapMethods属性。

对于支持 Class 文件格式版本号为 49.0 或更高的 Java 虚拟机实现,必须正确识别并读取 attributes表中的Signature、RuntimeVisibleAnnotations和RuntimeInvisibleAnnotations属性。对于支持Class文件格式版本号为 51.0 或更高的 Java 虚拟机实现,必须正确识别并读取 attributes表中的BootstrapMethods属性。Java 7 规范 要求 任一 Java 虚拟机实现可以自动忽略 Class 文件的 attributes表中的若干 (甚至全部) 它不可 识别的属性项。任何本规范未定义的属性不能影响Class文件的语义,只能提供附加的描述信息 。

 

5.以上就是JVM的基础的class文件的来源,很重要,需要记住。为后面的特征打好基础。后续会有类加载机制和运行数据区,垃圾回收机制,如有错误,可以留言更正!谢谢!

 

 

你可能感兴趣的:(Java,JVM学习,虚拟机)