IT数码 购物 网址 头条 软件 日历 阅读 图书馆
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
图片批量下载器
↓批量下载图片,美女图库↓
图片自动播放器
↓图片自动播放器↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁
 
   -> Java知识库 -> 深入理解jvm - 类加载过程 -> 正文阅读

[Java知识库]深入理解jvm - 类加载过程

前言

? 在最早的文章中,我们虽然讨论过了类加载器的过程,但是并没有讲述内部的细节,本文将会根据类加载器的过程,详细说一下整个类加载的过程中每一个步骤都干什么事情。

类加载过程图

? 类加载的过程如下:加载,验证,准备,初始化,解析,使用,卸载。重点需要关注的步骤是前面的五个步骤,这些细节算是八股文的内容,所以这篇文章以简单的总结和归纳为主。

概述

? 本篇主要讲述类加载的加载过程,在类加载的过程当中包含了前五个步骤和详细的细节。

类加载的过程

? 下们拆分这五个步骤,讲讲每一个步骤都做了哪些事情:

加载

? 第一步是加载,加载做的事情就是什么时候jvm需要去找到.class这个对象,我们都知道.class对象如果方法区中存在的话,java是不会去加载第二次的,那么类什么时候会进行初始化呢?

? 我们最容易想到的就是new的时候,所以可以肯定在new的时候会作为触发条件。接着我们有时候使用public static String mind = "xxx"这种常量的时候,有时候会构建常量类并且直接引用,这时候肯定也是需要先把对应的类加载过来的时候才可以使用的,最后既然我们使用其他类的静态字段会触发,那么使用其他类的静态方法肯定也是会触发类加载条件的。上面这三个条件,是我们最容易想到的三个,下面会稍微复杂一点点点触发加载动作的条件。

? 除了new之外,我们还知道一种方式是通过java的反射机制,其实就是拿到.class文件对应的类加载器去生成一个类,反射工具就是来简化这一个动作的,所以这里可以猜到,如果反射需要加载的类还不在方法,那肯定也要先把要加载的类加载进来才行。

? 我们从继承和实现两个角度去考虑什么时候会加载,从继承的角度看,如果父类没有被加载,那么父类也是要被加载进来的,至于为什么必须使用父类,这个问题类构造器可以作为解答,我们都知道在构造器的方法会执行一条super()的隐式方法,至于为什么要执行super()则是因为所有的类的父类都是Object。也是由于jvm的类加载器的设计所决定,双亲委派机制决定了所有的子类加载前需要加载父类。(注意,仅仅是加载,是否需要初始化下文会提到)

? 最后我们再来看下由于jdk版本带来的改进。首先是jdk7动态语言的支持,所有涉及new或者使用静态属性指令的类都会触发加载。而jdk8因为引入了接口的default方法(默认方法)让接口也可以完成“抽象类”的事情,所以如果有子类实现了拥有默认方法的接口,也是需要进行加载的。

? 下面我们总结上面关于加载的“初始化”条件:

  • New、静态字段引用、静态方法引用
  • 继承的父类,如果使用的是父类定义的字段或者方法时候会加载父类,但是不会加载子类。但是如果是但是如果是调用子类的,父类一定会被加载。
  • 反射机制生成的类需要加载(否则无法进行反射)。
  • jdk7动态语言涉及new和static的相关指令
  • jdk8实现了带有默认方法的接口的类。

最后,看一下书中给的一段加载“初始化”的代码,结果有点出乎意料哦:

package org.fenixsoft.classloading;
/**
* 被动使用类字段演示一:
* 通过子类引用父类的静态字段,不会导致子类初始化 **/
public class SuperClass {
	static {
		System.out.println("SuperClass init!");
	}
	public static int value = 123; 
}
public class SubClass extends SuperClass {
	static {
		System.out.println("SubClass init!");
	} 
}
/**
* 非主动使用类字段演示 **/
public class NotInitialization {
	public static void main(String[] args) { 
    System.out.println(SubClass.value);
	} 
}/*运行结果: SuperClass init! */

? 下面再看下如果只调用子类的静态方法会发生什么事情:

static class superClass{
    static {
        System.out.println("super load");
    }
}

static class SubClass extends superClass{
    static {
        System.out.println("sub load");
    }

    public static void test(){
        System.out.println("sdsad");
    }
}

public static void main(String[] args) {
 // write your code here
    SubClass.test();
}/**运行结果
	
*/

验证

? 验证是紧接着类加载之后的步骤,验证主要的做的事情是验证当前的class文件是否可以被虚拟机接受,这一步是至关重要的一步,决定了虚拟机是否安全,所以虚拟机规范里面用了N多页的内容讲述,当然这里的验证内容也是挑重点介绍。:

? 首先是验证文件格式,比如魔数,主次版本,常量池和索引,验证这些内容目的是防止有人篡改class文件结构。

? 验证完格式紧接着是验证具体的数据,比如类是否具备父类,以及验证定义的字段和属性等是否符合java的语法。这一节也叫做元数据验证,可以简单理解为对于语法等验证。

? 之后是字节码验证,也是最复杂的步骤,因为程序的运行依赖程序计数器扫描字节码指令完成,所以字节码验证主要的内容就是验证操作的“原子性”,比如Int操作不会变为long操作,同时保证栈帧的方法正常运行。

? 最后是符号引用的验证,验证是否能通过符号引用找到类的全限定名称,验证字段是否具备可访问性等。

? 总的来说,验证阶段分为下面等部分:

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

准备

? 容易误解的一个阶段,这个阶段需要注意的事情就是准备阶段初始化的静态变量是 final类型的静态常量。另外准备阶段会对类变量进行初始化,但是不会出现类的实例化,也就是说此时生成的仅仅是一个栈中的引用,可以通过引用在初始化阶段快速构建对象但是仅仅是做了一个准备而已,另外需要注意由于jdk7其实内部已经偷偷将常量池移动到了堆当中,所以这些变量都是在堆中生成的。

? 下面是关于静态变量的初始化细节:

private static final int count1 = 123;
private static int count2 = 55;

? 这里直接说结果,count在这个阶段的值是123,而count2是0。通过这个细节也可以说明为什么很多书中建议尽量使用final字段,因为它能将“初始化”的步骤提前。积少成多的情况下有不错的性能提升。

解析

? 解析的核心就是把符号引用变为直接引用,什么是符号引用,什么是直接引用呢?书中用了一大段内容描述,这里用一个案例来表示就明白了(请看下面的代码),obj就可以被认为是一个符号引用,符号引用可以是任何没有歧义的“占位符”(当然和关键字冲突是不行的),而直接引用就是将这个占位符指向一个堆中的实例,有了直接引用也证明实例在内存中已经开辟了空间,所以直接引用一定是一个指针并指向堆中一个实际地址:

public void test(){
  Object obj = null;
  obj = new Object();
}

这里有个唯一的例外:invokedy namic指令。这是java为了支持动态语言的特性而出现的一个指令,除开这个指令的所有其他指令都是可以认为解析这一步骤中已经实现了“静态化”,即指针具体指向的地址已经确定。

? 解析动作主要针对类或接口、字段、类方法、接口方法、方法类型、方法句柄和调用点限定符这7 类符号引用进行。

? 关于解析的细节,这里简要概括一下,当然这部分只需要了解四个主要步骤即可:

1. 类/接口解析
    不为数组,解析全限定名类加载器加载
    为数组,解析全限定名各数组元素,并生成对应数组维度的访问对象
2. 字段解析
    本类查找解析
    接口父类检查
    父类检查
    抛出nosuckField
3. 方法解析
    如果在class中发现class_index 索引为接口,会抛出异常
    本类查找
    父类查找
    父类与接口查找
    No such method
    返回直接引用进行权限验证
4. 接口方法解析
    与方法解析类似,注意解析到object类为止

初始化

? 注意这个步骤才是程序员真正认知意义上的初始化,也就是学习java基础的时候学到的初始化的顺序,同时也是真正执行java代码的阶段,所以这个阶段用“分配资源”这个词可能更加合适。

? 之前也提到过,准备阶段会有一个类变量的构建,可以认为是.class对象被加载到方法区,而初始化则是真正将方法区的这个引用构建到堆上。

? 这里不得不提<clinit >()这个方法,此方法是在编译时候由java生成的,简单理解可以认为是一个类的构造器的入口,所有的类初始化必须调用这个方法,同时如果发现父类没初始化,则需要执行父类的<clinit >(),最后如果是接口则在使用接口的常量的时候会调用<clinit >(),另外接口的实现类在初始化时也一样不会执行接口的<clinit >()方法。

类加载的细节

? 了解了类加载的过程,这一节来补充一些类加载的细节:

类加载基本条件

  • 加载/验证/准备三者顺序是确定的,原子化操作

Jvm只保证顺序 一致,但是不保证这三者的连贯性,意味着他们之间可以穿插其他的操作

  • 解析可能在初始化的前后

这是为了满足动态绑定的特性而设置的

  • 加载验证,准备并不是同步完成的,会存在交叉允许的情况

顺序确定,但是并不同步。

什么是被动引用?

  • 子类引用父类定义字段只会触发父类初始化

  • 数组初始化是newarray, 并不是合法对象初始化

  • 经过final修饰的常量池元素

本文主要围绕了类加载的过程这一个要点进行了复习,可以看到类加载的过程还是相对比较好理解的,需要特别关注的内容一个是准备阶段和初始化阶段,这里也有可能是一个踩坑点。

?最后

对于程序员来说,要学习的知识内容、技术有太多太多,要想不被环境淘汰就只有不断提升自己,从来都是我们去适应环境,而不是环境来适应我们!

不用多说,相信大家都有一个共识:无论什么行业,最牛逼的人肯定是站在金字塔端的人。所以,想做一个牛逼的程序员,那么就要让自己站的更高,成为技术大牛并不是一朝一夕的事情,需要时间的沉淀和技术的积累。

现在竞争这么激烈,只有通过不断学习,提高自己,才能保持竞争力。

对于一些不知道学习什么,没有一个系统路线的程序员,这里给大家提供一些学习资料

需要的小伙伴,可以一键三连,点击这里获取免费领取方式

《Java核心知识点合集(283页)》

内容涵盖:Java基础、JVM、高并发、多线程、分布式、设计模式、Spring全家桶、Java、MyBatis、ZooKeeper、Dubbo、Elasticsearch、Memcached、MongoDB、Redis、MySQL、RabbitMQ、Kafka、Linux、Netty、Tomcat、数据库、云计算等 在这里插入图片描述

《Java中高级核心知识点合集(524页)》

在这里插入图片描述

《Java高级架构知识点整理》

在这里插入图片描述

《Docker从入门到实践》

在这里插入图片描述

《spring could 学习笔记》

在这里插入图片描述

《JVM与性能调优知识点整理》

在这里插入图片描述

《MySQL性能调优与架构设计解析文档》305页

在这里插入图片描述

《Nginx入门到实战》319页

在这里插入图片描述

《Java并发编程》385页

在这里插入图片描述

《1000道 互联网Java工程师面试题 (485页)》

在这里插入图片描述

需要的小伙伴,可以一键三连,点击这里获取免费领取方式?

  Java知识库 最新文章
计算距离春节还有多长时间
系统开发系列 之WebService(spring框架+ma
springBoot+Cache(自定义有效时间配置)
SpringBoot整合mybatis实现增删改查、分页查
spring教程
SpringBoot+Vue实现美食交流网站的设计与实
虚拟机内存结构以及虚拟机中销毁和新建对象
SpringMVC---原理
小李同学: Java如何按多个字段分组
打印票据--java
上一篇文章      下一篇文章      查看所有文章
加:2021-08-20 14:57:12  更:2021-08-20 14:58:58 
 
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁

360图书馆 购物 三丰科技 阅读网 日历 万年历 2024年11日历 -2024/11/23 9:40:45-

图片自动播放器
↓图片自动播放器↓
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
图片批量下载器
↓批量下载图片,美女图库↓
  网站联系: qq:121756557 email:121756557@qq.com  IT数码