JVM真香系列:轻松理解class文件到虚拟机(上)

回复“000”获取大量电子书

JVM初探

class文件到JVM中,就相当于我们吃饭,食物吃进了肚子里,不同的营养成分被身体不同的器官吸收。

查找class文件并导入到JVM中

(1)通过一个类的全限定名,获取定义此类的二进制字节流
(2)将这个字节流所代表的静态存储结构,转化为方法区的运行时数据结构
(3)在Java堆中生成一个代表这个类的java.lang.Class对象,作为对方法区中这些数据的访问入口

获取class文件有哪些方式

.class文件也是需要查找的,以下是查找.class文件的常用方式:

  1. 从本地文件系统中加载.class文件

  2. 从jar包中或者war包中加载.class文件

  3. 通过网络或者从数据库中加载.class文件

  4. 把一个Java源文件动态编译,并加载

加载进来后就,系统为这个.class文件生成一个对应的Class对象。

生成Class对象的有哪些方式

1.对象获取:调用person类的父类方法getClaass();

2.类名获取,每个类型(包括基本类型和引用)都有一个静态属性,class。

3.Class类的静态方法获取。forName("字符串的类名")写全名,要带包名。 (包名.类名)

1/**
2 * 反射:就是通过class文件对象,去使用该文件中的成员变量,构造方法, 成员方法。
3 * 要想这样使用,首先你必须得到class文件对象,其实也就是得到Class类的对象。
4 * Class类:
5 * 成员变量 Field
6 * 构造方法 Constructor
7 * 成员方法 Method
8 * <p>
9 * 获取class文件对象的方式:
10 * A:Object类的getClass()方法
11 * B:数据类型的静态属性class
12 * C:Class类中的静态方法
13 * public static Class forName(String className)
14 * <p>
15 *  字符串:而不是一个具体的类名。这样我们就可以把这样的字符串配置到配置文件中。
16 */
17public class ClassDemo {
18 public static void main(String[] args) throws ClassNotFoundException {
19  // 方式1
20  User user= new User();
21  Class c = user.getClass();
22  User user1 = new User();
23  Class c2 = user1.getClass();
24  System.out.println(user == user1);// fals
25  System.out.println(c == c2);// true
26
27  // 方式2
28  Class c3 = User.class;
29  // int.class;
30  // String.class;
31  System.out.println(c == c3);
32
33  // 方式3
34  // ClassNotFoundException
35  //我们刚开始学jdbc的时候就见过这种方式
36  Class c4 = Class.forName("com.tian.demo.test.User");
37  System.out.println(c == c4);
38 }
39}

一个Class对象对应一个.class字节码文件。

比如说:User.class。当系统把它找到,并导入进来后,会为它生成一个对应的Class对象。

class字节码文件到Class对象的过程

回到官网中

https://docs.oracle.com/javase/specs/jvms/se8/html/

[Loading] 其实就是我们上面查找class文件并导入到JVM中。

[Linking] 就是对整个class内容进行一系列的校验、为一些变量进行数据准备、把字节码中符号进行解析等操作。

[Initializing] 就是初始化,创建我们使用的对象;User user=new User();

也是对应了网上很多文章以及很多书里所说的类加载过程分

其中链接这里包括验证、准备、解析。

所有就有了这么一个关系图:

上面介绍了这个class文件转载到JVM的过程,那么问题来了。什么问题?

可以自定义一个String类?

我们在自己的项目里创建一个目录java.lang。并在这个目录下创建一个String类。

1package java.lang;
2
3/**
4 * 咱们也搞个String类
5 *
6 * @author 田维常
7 * @version 1.0
8 * @date 2020/11/5 14:22
9 */
10public class String {
11 public static void main(String[] args) {
12  System.out.println("11111");
13 }
14}

这段代码看起来没毛病吧。但是然后运行这段代码居然报异常。

他居然说我的这个类里没有main方法。这不是扯淡吗,这个衣长如果你上网一查,发现说是我们没有编译成class文件

但是我们已经编译成String.class对象了。

这里并不是我们这里代码String类有问题,有问题的是Java自带String类,并且他是有限被找到的,所以当去Java中自带的String类中找main方法,肯定没有。

这就是如果我们系统里存在一个全路径名完全一样的类,类名称一毛一样的情况下,JVM是不知道我们到底要用哪个类。这个问题暂时先搁置,后面再细聊。

链接阶段做了什么?

JVM是如何把class文件里的东西存放的呢?

在官网的这个目录下

https://docs.oracle.com/javase/specs/jvms/se8/html/jvms-2.html#jvms-2.5

Method Area 方怡过来就是方法区,对,他就是很多人很害怕的JVM运行时数据区之一的方法区。

大致意思:

方法区是各个线程共享的内存区域,在虚拟机启动时创建。用于存储已被虚拟机加载的类信息、常量、静态变量、即时编译器编译后的代码等数据。虽然Java虚拟机规范把方法区描述为堆的一个逻辑部分,但是它却又一个别名叫做Non-Heap(非堆),目的是与Java堆区分开来。当方法区无法满足内存分配需求时,将抛出OutOfMemoryError异常。

通过这段翻译,我们就大致知道了,上面我们把.class字节码文件导入进来后是存放在方法区的。

导入.class字节码文件的时候,如果是你来解析,你觉得需要做些什么?

验证

首先肯定是要保证被加载类的正确性,也就是做一些校验罢了;

文件格式验证
元数据验证
字节码验证
符号引用验证

这些校验完毕,没问题,那么我们继续。

准备

为类的静态变量分配内存空间,并将其初始化为默认值。

比如说User.java中有个变量int a;

1 public class InitialDemo {
2  static int a = 10;
3
4  public static void main(String[] args) {
5   System.out.println(a);
6  }
7 }

在这个阶段,会对这些static修饰的变量进行赋值,附一个初始值,这里就是给

1int a =0;

因为int类型的初始值就是0;

如果是String类型,那么初始值就是null。

解析

初始值搞定后,还有就是有部分对象引用的,在.class字节码文件中还是符号,得给指定一个真实引用地址。

换言之,把符号引用变成直接引用。

符号引用

符号引用以一组符号来描述所引用的目标,符号可以是任何形式的字面量,只要使用时能够无歧义的定位到目标即可。

例如,在Class文件中通过javap命令能查看,它以

CONSTANT_Class_info

CONSTANT_Fieldref_info

CONSTANT_Methodref_info等类型的常量出现。

符号引用与虚拟机的内存布局无关,引用的目标并不一定加载到内存中。在Java中,一个java类将会编译成一个class文件。

在编译时,java类并不知道所引用的类的实际地址,因此只能使用符号引用来代替。

比如org.simple.People类引用了org.simple.Language类,在编译时People类并不知道Language类的实际内存地址,因此只能使用符号org.simple.Language(假设是这个,当然实际中是由类似于CONSTANT_Class_info的常量来表示的)来表示Language类的地址。

各种虚拟机实现的内存布局可能有所不同,但是它们能接受的符号引用都是一致的,因为符号引用的字面量形式明确定义在Java虚拟机规范的Class文件格式中。

直接引用

直接引用可以是以下三种场景:

(1)直接指向目标的指针(比如,指向“类型”【Class对象】、类变量、类方法的直接引用可能是指向方法区的指针)

(2)相对偏移量(比如,指向实例变量、实例方法的直接引用都是偏移量)

(3)一个能间接定位到目标的句柄

直接引用是和虚拟机的布局相关的,同一个符号引用在不同的虚拟机实例上翻译出来的直接引用一般不会相同。

如果有了直接引用,那引用的目标必定已经被加载入内存中了。

初始化过程

对类的静态变量,静态代码块执行初始化操作。

1 public class InitialDemo {
2  static int a = 10;
3
4  public static void main(String[] args) {
5   System.out.println(a);
6  }
7 }

这是才正式的给a赋值为10;

在准备阶段是给静态变量a附一个初始值,在初始化阶段才给变量a定义的值。

其初始化顺序为:

面试题

下面这段代码能否通过编译?能编译的话,输出什么?

1public class InitialDemo {
2
3 static int a = 0;
4
5 static {
6  a = 1;
7  b = 2;
8 }
9
10 static int b = 0;
11
12 public static void main(String[] args) {
13  System.out.println(a);
14  System.out.println(b);
15 }
16
17}

结果是:

11
20

a 和 b 唯一的区别就是它们的 static 代码块的位置。

这个结果需要结合准备阶段来理解才更好理解为什么输出1和0.

今天暂时讲到这里,《class文件到虚拟机》相关内容还有挺多,我们明天再接着聊~

(0)

相关推荐

  • 深入理解Java虚拟机系列笔记

    类加载过程 最近开始学习Java虚拟机,今天学习了类加载的三个过程,遂写一篇博客作为学习笔记 类加载子系统概述 类加载子系统作为JVM的一部分,负责将硬盘中的class字节码文件加载到JVM中.类加载 ...

  • Java语言中的修饰符—static

    没有经过全文修饰的文章,读起来就会显得苍白无力,而善于用贴切修饰词的文章则能更形象地呈现主题,而在Java编程中也提供了一些修饰语,它们可以修饰类.变量和方法.对修饰符的灵活使用将大大提高软件的重用性 ...

  • java基础:入门案例 HelloWorld

    java基础:入门案例 HelloWorld

  • JVM真香系列:轻松理解class文件到虚拟机(下)

    回复"000"获取大量电子书 类加载器 类加载器是很多人认为很硬的骨头.其实也没那么可怕,请听老田慢慢道来. 在装载(Load)阶段,通过类的全限定名获取其定义的二进制字节流,需要 ...

  • JVM真香系列:轻松掌握JVM运行时数据区

    回复"000"获取大量电子书 前面我们讲了从java源文件到class文件,在从class文件到JVM.那么今天继续聊JVM是如何布局的. JVM运行时数据区有几个?看看官网是就知 ...

  • JVM真香系列:.java文件到.class文件

    回复"000"获取大量电子书 认识JVM 什么是JVM JVM 全称 Java Virtual Machine,也就是我们耳熟能详的 Java 虚拟机.它能识别 .class后缀的 ...

  • JVM真香系列:图解垃圾回收器

    回复"000"获取大量电子书 不知不觉,JVM系列已经到回收算法的实现了. 本文主要内容 先普及三个概念: 并行收集:指多条垃圾收集线程并行工作,但此时用户线程仍处于等待状态. 并 ...

  • JVM真香系列:如何判断对象是否可被回收?

    回复"000"获取大量电子书 在JVM中程序寄存器.Java虚拟机栈.本地方法栈,这三个区是随着线程的创建而创建,随着线程结束而销毁. 其实就是这三个的生命周期和线程的生命周期一样 ...

  • JVM真香系列:堆内存详解

    回复"000"获取大量电子书 前面的文章中已经有所提到过堆,只是大致介绍了一下.本文就来详细聊聊JVM中的堆. 在 JVM中,堆被划分成两个不同的区域:新生代 ( Young ). ...

  • JVM真香系列:方法区、堆、栈之间到底有什么关系

    回复"000"获取大量电子书 栈指向堆 如果在栈帧中有一个变量,类型为引用类型,比如: package com.tian.my_code.test; public class Jv ...

  • 真香系列之盗版的WPS?

    ‍‍ ‍‍逃离工地后明显觉得能写的素材少了. 我琢磨了下原因,发现是因为到格子间后,面对的就那几个固定的同事,故事有限:有趣程度也远比不上工地上的那些个吃喝嫖赌抽.吃拿卡要偷的主儿. 这就让我有点伤脑 ...

  • 10万元左右就能买!这款合资SUV实锤"真香"系列

    如何在有限的预算下,购买一辆称心如意的新座驾?成为当前年轻消费者普遍的难题.以10-15万级别的SUV为例,这里面既有主打高配置的国产SUV,也有主打品牌和品质的合资SUV,到底哪款车最值得年轻人入手 ...