JVM底层原理学习(一)之JVM概述

JVM底层原理

作为一个Java程序猿,如果每天都在用java去跟业务逻辑做周旋,而不知其中奥妙所在,实在是一件遗憾的事情。不论是为了面试、调优、亦或是为了理解整个java的运行体系,都应该对java平台的基石–JVM有一个了解,要知其然,知其所以然。这样学知识才能通透,如果死记硬背,那就会像鱼的记忆一样,7秒就忘。

JVM到底是什么?

我觉得要想搞清楚这个问题,最好的方法就是去问问官网

如下图所示:
JVM官网描述
官网给出的定义就是上面这一大堆英文,英文不好的我,只看懂了第一句:“java虚拟机是java平台的基石”,幸好有百度翻译,翻译过后的意思大概如下:

  • JVM是java平台的基石,它屏蔽了操作系统和硬件,并使我们的程序免受恶意攻击。
  • JVM是一个抽象的计算机,他像真正的计算机一样,都有指令集,并且在运行时操作着各种内存空间。
  • JVM虚拟机对java编程语言一无所知,它只认识一种特殊的二进制格式,类文件格式。
  • 为了安全起见,JVM对类文件中的代码施加了严格的语法和结构约束(所以java是强类型语言),JVM能够运行可用的有效的class字节码文件,JVM是一个多语言的平台。

JAVA如何做到跨操作系统和平台?

java程序是运行在JVM上的 ,由JVM去屏蔽不同操作系统之间的差异,不论是window、linux、还是别的系统,只要是安装了jdk,那么就可以运行java字节码文件

JVM是Java语言能做到(Write Once Run Anywhere)跨平台的根本
跨平台示意图

JVM、JDK、JRE三者之间的关系

从官网上可以找到图解
在这里插入图片描述
可以看到,JDK包含JRE包含JVM

JDK 8 is a superset of JRE 8, and contains everything that is in JRE 8, plus tools such as the 
compilers and debuggers necessary for developing applets and applications. JRE 8 provides the 
libraries, the Java Virtual Machine (JVM), and other components to run applets and applications 
written in the Java programming language. Note that the JRE includes components not required by
the Java SE specification, including both standard and non-standard Java components.

翻译:
JDK是一个最大的超集,(Java Development Kit)是针对Java开发员的产品,是整个Java的核心,包括了Java运行环境JRE、Java工具 和Java基础类库。
Java Runtime Environment(JRE)是运行JAVA程序所必须的环境的集合,包含JVM标准实现及Java核心类库。
JVM是Java Virtual Machine(Java虚拟机),是整个java实现跨平台的最核心的部分,能够运行遵守JVM规范的软件程序。

JVM概述

  • java代码运行的几个阶段
    在这里插入图片描述
源文件编译(javac)

Test.java -> Test.class 的过程
词法分析器 -> 语法分析器 -> 语法树/抽象语法树 -> 语义分析器 -> 注解抽象语法树 -> 字节码生成器 ->class文件。
这个其实就是大学中学过的编译原理那门课程,如果不是要创造属于自己的语言,对这块内容了解即可。

字节码文件分析(.class文件)

直接通过文本工具打开字节码文件(Test.class),发现并不能正常打开,出现了乱码.一般的文本文件是不能打开.class文件的。
在这里插入图片描述
所以我们换一种可以打开二进制文件的工具,我这里用的是Sublime
在这里插入图片描述
这次可以打开,但全部都是二进制码,还是看不懂,还是去官网寻求下答案吧。
文档连接:官网链接

官方规范定义的格式(The class File Format):

ClassFile {
    u4             magic;
    u2             minor_version;
    u2             major_version;
    u2             constant_pool_count;
    cp_info        constant_pool[constant_pool_count-1];
    u2             access_flags;
    u2             this_class;
    u2             super_class;
    u2             interfaces_count;
    u2             interfaces[interfaces_count];
    u2             fields_count;
    field_info     fields[fields_count];
    u2             methods_count;
    method_info    methods[methods_count];
    u2             attributes_count;
    attribute_info attributes[attributes_count];
}

在这里插入图片描述
让我们来一起分析下

u4 magic
The magic item supplies the magic number identifying the class file format; it has the value
0xCAFEBABE.

magic提供标识类文件格式它的值是固定的CAFEBABE

在这里插入图片描述

u2 minor_version, u2 major_version
The values of the minor_version and major_version items are the minor and major version numbers
of this class file

当前class文件的支持的jdk的版本号:minor_version是最小版本 major_version是最高的版本.

最低版本:十六进制的 0000 -> 转换成十进制还是0
最高版本:十六进制的 0034 -> 转换成十进制是52

在这里插入图片描述
在这里插入图片描述
那就是说,当前这个class支持的jdk版本号 最小支持0,最大支持52,也就是jdk1.8
继续往下看

u2 constant_pool_count
The value of the constant_pool_count item is equal to the number of entries in the constant_pool 
table plus one. A constant_pool index is considered valid if it is greater than zero and less than 
constant_pool_count, with the exception for constants of type long and double noted in §4.4.5.
常量个数

constant_pool[]
The constant_pool is a table of structures (§4.4) representing various string constants, class and
interface names, field names, and other constants that are referred to within the ClassFile structure
and its substructures. The format of each constant_pool table entry is indicated by its first "tag" byte.

The constant_pool table is indexed from 1 to constant_pool_count - 1.

常量池是一个结构表,他包含字符串常量、类和接口名称、字段名以 及在类文件结构及其子结构中引用的其他常量。 每个常量池中的每一项的格式由其第一个“标记”字节表示。
常数池表的索引是从1开始的所以常数池中常量的个数为 constant_pool_count-1

常量池主要存储两方面内容:字面量(Literal)和符号引用(Symbolic References)

字面量:文本字符串,final修饰等 
符号引用:类和接口的全限定名、字段名称和描述符、方法名称和描述

就这样一点一点的去对文档,去百度翻译,基本上可以看懂整个class文件。但是这样一是太耗时,二是没必要。我们可以借助工具去直接翻译成我们人类可以看懂的文字

用javap工具分析字节码

这个工具是jdk给我们提供的反编译工具,用于查看字节码信息和指令信息
使用方式:

javap -v -p Test.class > Test.txt
其中:
-v的意思是:查看字节码结构
-p的意思是:将额外的打印字节码信息

在这里插入图片描述
打开Test2.txt

Classfile /Users/xuehuichen/codingProjects/ideaProject/test/target/classes/com/xhc/test/Test.class
  Last modified 2020-8-15; size 569 bytes
  MD5 checksum 6a6dfaba43dd01223d04bd027262941f
  Compiled from "Test.java"
public class com.xhc.test.Test
  minor version: 0
  major version: 52
  flags: ACC_PUBLIC, ACC_SUPER
Constant pool:
   #1 = Methodref          #6.#21         // java/lang/Object."<init>":()V
   #2 = Fieldref           #22.#23        // java/lang/System.out:Ljava/io/PrintStream;
   #3 = String             #24            // 你真好
   #4 = Methodref          #25.#26        // java/io/PrintStream.println:(Ljava/lang/String;)V
   #5 = Class              #27            // com/xhc/test/Test
   #6 = Class              #28            // java/lang/Object
   #7 = Utf8               <init>
   #8 = Utf8               ()V
   #9 = Utf8               Code
  #10 = Utf8               LineNumberTable
  #11 = Utf8               LocalVariableTable
  #12 = Utf8               this
  #13 = Utf8               Lcom/xhc/test/Test;
  #14 = Utf8               main
  #15 = Utf8               ([Ljava/lang/String;)V
  #16 = Utf8               args
  #17 = Utf8               [Ljava/lang/String;
  #18 = Utf8               MethodParameters
  #19 = Utf8               SourceFile
  #20 = Utf8               Test.java
  #21 = NameAndType        #7:#8          // "<init>":()V
  #22 = Class              #29            // java/lang/System
  #23 = NameAndType        #30:#31        // out:Ljava/io/PrintStream;
  #24 = Utf8               你真好
  #25 = Class              #32            // java/io/PrintStream
  #26 = NameAndType        #33:#34        // println:(Ljava/lang/String;)V
  #27 = Utf8               com/xhc/test/Test
  #28 = Utf8               java/lang/Object
  #29 = Utf8               java/lang/System
  #30 = Utf8               out
  #31 = Utf8               Ljava/io/PrintStream;
  #32 = Utf8               java/io/PrintStream
  #33 = Utf8               println
  #34 = Utf8               (Ljava/lang/String;)V
{
  public com.xhc.test.Test();
    descriptor: ()V
    flags: ACC_PUBLIC
    Code:
      stack=1, locals=1, args_size=1
         0: aload_0
         1: invokespecial #1                  // Method java/lang/Object."<init>":()V
         4: return
      LineNumberTable:
        line 10: 0
      LocalVariableTable:
        Start  Length  Slot  Name   Signature
            0       5     0  this   Lcom/xhc/test/Test;

  public static void main(java.lang.String[]);
    descriptor: ([Ljava/lang/String;)V
    flags: ACC_PUBLIC, ACC_STATIC
    Code:
      stack=2, locals=1, args_size=1
         0: getstatic     #2                  // Field java/lang/System.out:Ljava/io/PrintStream;
         3: ldc           #3                  // String 你真好
         5: invokevirtual #4                  // Method java/io/PrintStream.println:(Ljava/lang/String;)V
         8: return
      LineNumberTable:
        line 12: 0
        line 13: 8
      LocalVariableTable:
        Start  Length  Slot  Name   Signature
            0       9     0  args   [Ljava/lang/String;
    MethodParameters:
      Name                           Flags
      args
}
SourceFile: "Test.java"

这样就看的清楚了,我们试着接着自己解析下,看看和这个解析出来的文件能不能对上。
在这里插入图片描述
在这里插入图片描述
接着往下面数一个u1

cafe babe 0000 0034 0023(35-1) 0a(10代表的是CONSTANT_Methodref 方法引用)
00 06(第六行)00 15(第21行)09
0016 0017 0800 180a 0019 001a 0700 1b07
001c 0100 063c 696e 6974 3e01 0003 2829
5601 0004 436f 6465 0100 0f4c 696e 654e
756d 6265 7254 6162 6c65 0100 124c 6f63
616c 5661 7269 6162 6c65 5461 626c 6501
0004 7468 6973 0100 134c 636f 6d2f 7868
632f 7465 7374 2f54 6573 743b 0100 046d
6169 6e01 0016 285b 4c6a 6176 612f 6c61
6e67 2f53 7472 696e 673b 2956 0100 0461
7267 7301 0013 5b4c 6a61 7661 2f6c 616e
672f 5374 7269 6e67 3b01 0010 4d65 7468
6f64 5061 7261 6d65 7465 7273 0100 0a53
6f75 7263 6546 696c 6501 0009 5465 7374
2e6a 6176 610c 0007 0008 0700 1d0c 001e
001f 0100 09e4 bda0 e79c 9fe5 a5bd 0700
200c 0021 0022 0100 1163 6f6d 2f78 6863
2f74 6573 742f 5465 7374 0100 106a 6176
612f 6c61 6e67 2f4f 626a 6563 7401 0010
6a61 7661 2f6c 616e 672f 5379 7374 656d
0100 036f 7574 0100 154c 6a61 7661 2f69
6f2f 5072 696e 7453 7472 6561 6d3b 0100
136a 6176 612f 696f 2f50 7269 6e74 5374
7265 616d 0100 0770 7269 6e74 6c6e 0100
1528 4c6a 6176 612f 6c61 6e67 2f53 7472
696e 673b 2956 0021 0005 0006 0000 0000
0002 0001 0007 0008 0001 0009 0000 002f
0001 0001 0000 0005 2ab7 0001 b100 0000
0200 0a00 0000 0600 0100 0000 0a00 0b00
0000 0c00 0100 0000 0500 0c00 0d00 0000
0900 0e00 0f00 0200 0900 0000 3700 0200
0100 0000 09b2 0002 1203 b600 04b1 0000
0002 000a 0000 000a 0002 0000 000c 0008
000d 000b 0000 000c 0001 0000 0009 0010
0011 0000 0012 0000 0005 0100 1000 0000
0100 1300 0000 0200 14

接着再对应刚刚编译好的文件(注意图中标红的框)
在这里插入图片描述
是不是发现这些数字就是刚才算出来的数字。
所以说明javap这个工具也是这么一点点对照出来的,就像我们推出来的一样。
我这里只是做个演示。其实任何技术都不难,懂得了其中的原理,不用死记硬背,也知道是怎么回事。

JVM底层原理学习(二)之类加载子系统

  • 3
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值