阅读 87

初识 JVM(带你从不同的视角认识 JVM)

概述

一说到 JVM 三个字母,你脑子里首先蹦出来的是什么?我分析一般有以下三种人:

  • 第一种:JVM 三个字母,分开我是认识的,一组合,我不知道是啥

  • 第二种:他不就是 Java 虚拟机么,跑 Java 程序的

  • 第三种:分为堆内存,方法区,老年代,新生代.....巴拉巴拉...,可以跟你说几个小时的

经过我的日常观察,一般第一种人是程序猿的家属,第二种人是程序猿的同事(非 Java 工作的同事),第三种人就是程序猿本猿了,而本文的适合读者是第三种人,我试图从不同的视角出发,带领程序猿们站在另外一个高度点重新全面的重新认识一次 JVM,从浅到深的逐步深入,再一次对我们认知的 JVM 做一个全面的梳理,不仅知道它有堆内存、方法区、老年代、新生代...更要知道其原理,也要知道其使用的方法。在深入理解其设计原理的基础上,再对它进行使用和调优,就会更加的得心应手。

接下来,让我逐步带领大家进入 JVM 的世界!后续我将以 JVM 系列文章的形式,逐步带大家从浅到深的认识 JVM。

声明:以下内容篇幅较长,请耐心看完,你一定会有不一样的收获!

一切从官网开始

那么认识 JVM 的第一步该是什么呢?有很多人第一次认识 JVM 是在百度或者 google 上的,其实第一步你就错了,我认为一切应该还是从官网开始,从它出生的地方先做一个全面的了解。

Java Platform Standard Edition 8 Documentation

官网地址:docs.oracle.com/javase/8/do…

Reference -> Developer Guides -> 定位到:docs.oracle.com/javase/8/do…

Tips:花点时间,耐心的读完这段话,你会有新的心得的!

Oracle has two products that implement Java Platform Standard Edition (Java SE) 8: Java SE Development Kit (JDK) 8 and Java SE Runtime Environment (JRE) 8.

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.

The following conceptual diagram illustrates the components of Oracle's Java SE products:

Description of Java Conceptual Diagram

Getting Started with the G1 Garbage Collector

官网地址:www.oracle.com/technetwork…

Exploring the JVM Architecture

Hotspot Architecture

The HotSpot JVM possesses an architecture that supports a strong foundation of features and capabilities and supports the ability to realize high performance and massive scalability. For example, the HotSpot JVM JIT compilers generate dynamic optimizations. In other words, they make optimization decisions while the Java application is running and generate high-performing native machine instructions targeted for the underlying system architecture. In addition, through the maturing evolution and continuous engineering of its runtime environment and multithreaded garbage collector, the HotSpot JVM yields high scalability on even the largest available computer systems.

HotSpot JVM Architecture

The main components of the JVM include the class loader, the runtime data areas, and the execution engine.

阅读理解一下(摘自上文):

原文:

In other words, they make optimization decisions while the Java application is running and generate high-performing native machine instructions targeted for the underlying system architecture

翻译:

换句话说,它能在 Java 应用程序运行时做出优化决策,并生成针对底层系统架构的高性能本地机器指令。

意思就是说,JVM 的最大能力是,针对底层不同的操作系统,将 Java 程序翻译成不同操作系统能够认识和执行的本地机器指令,提供给操作系统进行运行。

以下这个图对于认识 JVM 的人来说一点都不陌生(一次编译到处运行):

源码到类文件,再到字节码

我们先写一个最简单的 HelloWorld.java,编译一下,得到 HelloWorld.class 文件。以上两个步骤是我们每一个 Java 程序猿最清楚不过的了,一个是.java 结尾的源码文件,一个是.class 结尾的字节码文件,我们都知道一个 java 程序要想运行起来,必须经过编辑器编译成.class 字节码文件,JVM 才可以运行起来。但是你有没有想过,这个.class 结尾的字节码文件是如何运行起来,并且在控制台上打印出“Hello World”的呢?

别急,通过以下几个步骤的解读,让我带领大家进入 JVM 的世界,从不同的角度来重新认识一次 JVM。

public class HelloWorld {     public static void main(String[] args) {         System.out.println("Hello World");     } } 复制代码

查看一个 class 文件字节码

我们先通过 JDK 自带的命令:hexdump -C HelloWorld.class  来查看下字节码。

输入以上命令后,输出以下内容,现在这一堆东西,你一定看不懂,不急,先看下官网是怎么说的,看下面内容。

00000000  ca fe ba be 00 00 00 34  00 22 0a 00 06 00 14 09  |.......4."......| 00000010  00 15 00 16 08 00 17 0a  00 18 00 19 07 00 1a 07  |................| 00000020  00 1b 01 00 06 3c 69 6e  69 74 3e 01 00 03 28 29  |.....<init>...()| 00000030  56 01 00 04 43 6f 64 65  01 00 0f 4c 69 6e 65 4e  |V...Code...LineN| 00000040  75 6d 62 65 72 54 61 62  6c 65 01 00 12 4c 6f 63  |umberTable...Loc| 00000050  61 6c 56 61 72 69 61 62  6c 65 54 61 62 6c 65 01  |alVariableTable.| 00000060  00 04 74 68 69 73 01 00  0c 4c 48 65 6c 6c 6f 57  |..this...LHelloW| 00000070  6f 72 6c 64 3b 01 00 04  6d 61 69 6e 01 00 16 28  |orld;...main...(| 00000080  5b 4c 6a 61 76 61 2f 6c  61 6e 67 2f 53 74 72 69  |[Ljava/lang/Stri| 00000090  6e 67 3b 29 56 01 00 04  61 72 67 73 01 00 13 5b  |ng;)V...args...[| 000000a0  4c 6a 61 76 61 2f 6c 61  6e 67 2f 53 74 72 69 6e  |Ljava/lang/Strin| 000000b0  67 3b 01 00 0a 53 6f 75  72 63 65 46 69 6c 65 01  |g;...SourceFile.| 000000c0  00 0f 48 65 6c 6c 6f 57  6f 72 6c 64 2e 6a 61 76  |..HelloWorld.jav| 000000d0  61 0c 00 07 00 08 07 00  1c 0c 00 1d 00 1e 01 00  |a...............| 000000e0  0b 68 65 6c 6c 6f 20 77  6f 72 6c 64 07 00 1f 0c  |.hello world....| 000000f0  00 20 00 21 01 00 0a 48  65 6c 6c 6f 57 6f 72 6c  |. .!...HelloWorl| 00000100  64 01 00 10 6a 61 76 61  2f 6c 61 6e 67 2f 4f 62  |d...java/lang/Ob| 00000110  6a 65 63 74 01 00 10 6a  61 76 61 2f 6c 61 6e 67  |ject...java/lang| 00000120  2f 53 79 73 74 65 6d 01  00 03 6f 75 74 01 00 15  |/System...out...| 00000130  4c 6a 61 76 61 2f 69 6f  2f 50 72 69 6e 74 53 74  |Ljava/io/PrintSt| 00000140  72 65 61 6d 3b 01 00 13  6a 61 76 61 2f 69 6f 2f  |ream;...java/io/| 00000150  50 72 69 6e 74 53 74 72  65 61 6d 01 00 07 70 72  |PrintStream...pr| 00000160  69 6e 74 6c 6e 01 00 15  28 4c 6a 61 76 61 2f 6c  |intln...(Ljava/l| 00000170  61 6e 67 2f 53 74 72 69  6e 67 3b 29 56 00 21 00  |ang/String;)V.!.| 00000180  05 00 06 00 00 00 00 00  02 00 01 00 07 00 08 00  |................| 00000190  01 00 09 00 00 00 2f 00  01 00 01 00 00 00 05 2a  |....../........*| 000001a0  b7 00 01 b1 00 00 00 02  00 0a 00 00 00 06 00 01  |................| 000001b0  00 00 00 06 00 0b 00 00  00 0c 00 01 00 00 00 05  |................| 000001c0  00 0c 00 0d 00 00 00 09  00 0e 00 0f 00 01 00 09  |................| 000001d0  00 00 00 37 00 02 00 01  00 00 00 09 b2 00 02 12  |...7............| 000001e0  03 b6 00 04 b1 00 00 00  02 00 0a 00 00 00 0a 00  |................| 000001f0  02 00 00 00 08 00 08 00  09 00 0b 00 00 00 0c 00  |................| 00000200  01 00 00 00 09 00 10 00  11 00 00 00 01 00 12 00  |................| 00000210  00 00 02 00 13                                    |.....| 00000215 复制代码

The ClassFile Structure(字节码解析)

看看官网是如何解释字节码文件的:

官网地址:docs.oracle.com/javase/spec…

A class file consists of a single ClassFile structure:

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;

  • u4:代表 4个字节 Byte(0x cafebabe),他是个魔数(不具有业务含义,只是表明了该文件的类型)

基础知识:

一个字节存储 8位无符号数,储存的数值范围为 0-255。1111 0000(为 1个字节)

1Byte = 8bit

1Byte = 2 位16 进制

4Byte = 0x cafebabe

所以,u4 magic,就是 0x cafebabe,转换为二进制为:1100 1010 1111 1110 1011 1010 1011 1110

cafebabe

第二个和第三个 ,u2 minor_version; u2 major_version; (合并组合为版本号)

  • u2 minor_version:代表 2个字节(0x 00 00),代表小版本号

  • u2 major_version:代表 2个字节(0x 00 34),代表主版本号

  • 主版本号+小版本号合并起来,组成版本号,0x 00000034 -->转换成十进制就是:52(查找对应表,表示版本号为:Java SE8)

版本号对应表

第四个,u2 constant_pool_count;  表示常量数量

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.

  • u2 constant_pool_count:代表 2个字节(0x 00 22),转换十进制值为:34,代表常量池中,常量的数量有 34-1=33 个

  • 那么这个类里面到底有哪些常量呢?  看下面的常量数组:

第五个,cp_info constant_pool[constant_pool_count-1];   表示常量数组

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.

  • 每个常量的形式:

The Constant Pool

Java Virtual Machine instructions do not rely on the run-time layout of classes, interfaces, class instances, or arrays. Instead, instructions refer to symbolic information in the constant_pool table.

All constant_pool table entries have the following general format:

cp_info {     u1 tag;      //表示:常量的类型     u1 info[];   //表示: } 复制代码

第一个常量:

  • u1 tag:代表 1个字节(0x 0a),转换为十进制值为:10,找到以下对应表,表示是一个方法类型的常量

// 方法类型常量 CONSTANT_Methodref_info {     u1 tag;     u2 class_index;     u2 name_and_type_index; } 复制代码

常量编号u1 tag常量类型常量内容
#10x 0a-->10CONSTANT_Methodrefu2 class_index:0x 00 06--> #6
u2 name_and_type_index:0x 00 14 --> #20
#6.#20

常量类型对应表

其他常量解析:(按照上面的方法,一步一步,就可以解析出来这样一张表格)

到现在为止,你一定还是看不懂解析出这一堆有啥用,不着急,先看下面的一个命令。

常量编号u1 tag常量类型常量内容
#10x 0a-->10CONSTANT_Methodrefu2 class_index:0x 00 06--> #6
u2 name_and_type_index:0x 00 14 --> #20
#6.#20
#20x 09-->9CONSTANT_Fieldrefu2 class_index:0x 00 15--> #21
u2 name_and_type_index:0x 00 16-> #22
#21.#22
#30x 08-->8CONSTANT_Stringu2 string_index:0x 00 17--> #23
#40x 0a-->10CONSTANT_Methodrefu2 class_index:0x 00 18--> #24
u2 name_and_type_index:0x 00 19 --> #25
#24.#25
...


#33


javap 反汇编

  • 命令:javap -v -p -c HelloWorld.class > HelloWorld_javap.txt  (将 HelloWorld 字节码文件进行反汇编,生成的内容写入 HelloWorld_javap.txt)

// 截取部分内容如下: Classfile /.../HelloWorld.class   Last modified 2021-9-28; size 533 bytes   MD5 checksum c3ef731e76bb9c516e7840a04a0c71af           //<-----------magic   Compiled from "HelloWorld.java" public class HelloWorld   minor version: 0   major version: 52                       //<-----------------版本号   flags: ACC_PUBLIC, ACC_SUPER Constant pool:    #1 = Methodref          #6.#20         // java/lang/Object."<init>":()V    #2 = Fieldref           #21.#22        // java/lang/System.out:Ljava/io/PrintStream;    #3 = String             #23            // hello world    #4 = Methodref          #24.#25        // java/io/PrintStream.println:(Ljava/lang/String;)V    #5 = Class              #26            // HelloWorld    #6 = Class              #27            // java/lang/Object    #7 = Utf8               <init>    #8 = Utf8               ()V    #9 = Utf8               Code   #10 = Utf8               LineNumberTable    ...    #33    ... 复制代码

小结

从上面我们的分析,我们大概知道了一个 Java 源文件需要首先编译成 class 文件后,JVM 虚拟机才能真正的运行这段代码的一个全过程,那么接下来,我们要研究下,class 文件是如何被加载到 JVM 的,Java 类加载机制。

敬请期待,JVM 系列文章第二章,JVM 类加载机制(从不同的视角进行解读)!

推荐阅读

Guava Cache实战—从场景使用到原理分析

详解 HTTP2.0 及 HTTPS 协议

招贤纳士

政采云技术团队(Zero),一个富有激情、创造力和执行力的团队,Base 在风景如画的杭州。团队现有300多名研发小伙伴,既有来自阿里、华为、网易的“老”兵,也有来自浙大、中科大、杭电等校的新人。团队在日常业务开发之外,还分别在云原生、区块链、人工智能、低代码平台、中间件、大数据、物料体系、工程平台、性能体验、可视化等领域进行技术探索和实践,推动并落地了一系列的内部技术产品,持续探索技术的新边界。此外,团队还纷纷投身社区建设,目前已经是 google flutter、scikit-learn、Apache Dubbo、Apache Rocketmq、Apache Pulsar、CNCF Dapr、Apache DolphinScheduler、alibaba Seata 等众多优秀开源社区的贡献者。如果你想改变一直被事折腾,希望开始折腾事;如果你想改变一直被告诫需要多些想法,却无从破局;如果你想改变你有能力去做成那个结果,却不需要你;如果你想改变你想做成的事需要一个团队去支撑,但没你带人的位置;如果你想改变本来悟性不错,但总是有那一层窗户纸的模糊……如果你相信相信的力量,相信平凡人能成就非凡事,相信能遇到更好的自己。如果你希望参与到随着业务腾飞的过程,亲手推动一个有着深入的业务理解、完善的技术体系、技术创造价值、影响力外溢的技术团队的成长过程,我觉得我们该聊聊。任何时间,等着你写点什么,发给 zcy-tc@cai-inc.com

微信公众号

文章同步发布,政采云技术团队公众号,欢迎关注

image.png


作者:政采云技术团队
链接:https://juejin.cn/post/7054327769118801956

文章分类
代码人生
文章标签
版权声明:本站是系统测试站点,无实际运营。本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌抄袭侵权/违法违规的内容, 请发送邮件至 XXXXXXo@163.com 举报,一经查实,本站将立刻删除。
相关推荐