美文网首页
白话类加载

白话类加载

作者: 瑞瑞余之 | 来源:发表于2019-11-21 16:19 被阅读0次

无论你看哪个版本JVM书籍,类加载是绕不开的开篇第一课,然而我们对其理解往往受限于JVM繁复的概念,而无法真正消化,本文力求图文结合,用大白话让读者真正理解JVM类加载阶段。
JVM类加载的阶段可以分为:加载、连接、初始化。标准的类加载阶段其实包括了5个步骤,其中连接又分为验证、准备、解析。


类加载阶段

我们一步步来理解:当我们的Java源文件(.java)文件被编译器编译成.class的字节码文件后,这个字节码文件还是存在于我们的硬盘上,我们可以在项目结构中看到这些编译后的class文件:


字节码文件

加载

java虚拟机要操作它,第一步当然是将硬盘上的字节码文件放到内存当中,这个过程就是类加载过程的第一步加载(在刚学习JVM的时候,往往把类加载过程和这个加载阶段混淆,客观来说,我也觉得这两个加载取名不够好)。这里有几点需要明确!

  • .class文件加载到内存中什么区域?
    系统内存会分配给JVM一块专门的工作内存,我们称之为运行时数据区,这个区域对于JVM的学习来说尤其重要,这里先针对问题简单介绍,后面会详细解释各部分的功能,在运行时数据区中有一块区域称之为方法区或者元空间,经过第一步加载,类的数据就进入了这个内存部分:

    类加载进入方法区
  • 怎么理解.class文件加载到内存当中?
    通过图示我们可以看到,方法区中存放的是class A的相关信息,具体包括:常量、静态变量、方法信息等等。这里我们再进一步:


    方法区中存放的类信息

    当类被加载到方法区之后,可以看到,一个类被解构到两个部分,可以看出,除了类中声明的常量进入了方法区中的常量池以外,class的其他信息以class标记,存放在方法区中。这就说清楚了一个问题,当我们在new一个对象的时候,JVM怎么知道这个对象长什么模样?答案就在这里,因为在方法区记录着这个类的所有基本信息!

  • 除此之外还有什么结果产生
    其实类在加载这个阶段,除了将类数据载入到方法区,并转化成方法区运行时数据结构以外,还有一个重要产物:在堆内存中生产一个代表这个类的java.lang.Class对象(注意这个对象是类的对象,而不是类的实例对象),作为方法区这类各种数据的访问入口。怎么理解这句话呢:如果你了解反射机制,应该知道我们可以通过反射的方式,根据类的定义创建对象实例,那么问题来了,我们通过Class.forName(全域名)这种方式如何获取到一个类的基本呢信息呢,前面我们说了,类的基本信息存放在方法区中,对于new一个对象实例,类的数据信息是从这里来的。而反射获取类信息的方式就是通过在加载阶段,堆内存中生成的java.lang.Class对象来进行获取。

说到这里我们了解了JVM会将编译好的字节码文件加载到虚拟机允许时环境的方法区当中,这些信息会从字节码文件格式转换成方法区运行时数据格式,最重要的是JVM会根据这个类的信息在堆内存当中创建一个类对象

到此为止加载的结果已经清楚了。但还有两个问题是需要解决的:1.类加载的时机,2.类加载的过程。我们先来看第一个问题:

1. 类加载的时机:

Java虚拟机规范对类加载的时机没有明确规定,但肯定是发生在运行时当中。看一些文章会将类的加载时机和类的初始化时机混淆,这里强调一下,类的加载最终目的是将class文件加载到方法区并在堆中创建对应的class对象。而类的初始化是给静态变量赋上正确的值,它发生在加载、连接之后,所以不是一回事。类的加载与类的初始化不同,它不需要等到该类被首次主动使用(后面会解释)时才去加载,JVM运行类加载器在预料到某个类要被使用时就提前加载它。这个时候如果加载出现了错误,JVM不会立即报错,而是在程序首次主动使用它的时候才报告错误,如果这个类一直没有被主动使用,则一直不会报错。

2. 类的加载过程:

首先,我们都知道类的加载是由类加载器完成的,JVM中加载器分为三类:Bootstrap、Extension、App,其中BootStap类加载器是基于JVM的也就是说不同类型的JVM不一样,可以理解成native;Extension和Application类加载器继承自ClassLoader类是Java代码编写的。每类ClassLoader负责加载不同位置的class

  • Bootstrap ClassLoader(爷爷):jre\lib\rt.jar
    我们知道Java JDK包基本都在rt.jar中,我们可以看到常用的java.lang,java.util都在里面:


    rt.jar
  • Extension ClassLoader(父亲): jre\lib\ext\*.jar
  • App ClassLoader(儿子):它是用来加载在我们环境变量下的class文件。
    这里我们介绍了JVM自带的ClassLoader,最后通过ClassLoader源码来看看类加载器之间是如何工作的。
    前面说了Extension和App类加载器都继承自Classloader.java。在ClassLoader的源码注释中,很好的解释了ClassLoader这个类是做什么工作:

A class loader is an object that is responsible for loading classes. The class ClassLoader is an abstract class. Given the binary name of a class, a class loader should attempt to locate or generate data that constitutes a definition for the class. A typical strategy is to transform the name into a file name and then read a "class file" of that name from a file system.Every Class object contains a reference to the ClassLoader that defined it.

译:类加载器是用来加载类文件的一个对象。ClassLoader这个class本身是一个抽象类,ClassLoader可以根据提供的类名定位到对应的类文件,最典型的方式就是将类名转换成文件名。每一个Class对象都可以访问到生成它的类加载器。

这里的Class对象不就是我们说的加载的最终成果么!!!那么ClassLoader如何工作呢:

The ClassLoader class uses a delegation model to search for classes and resources. Each instance of ClassLoader has an associated parent class loader. When requested to find a class or resource, a ClassLoader instance will delegate the search for the class or resource to its parent class loader before attempting to find the class or resource itself. The virtual machine's built-in class loader, called the "bootstrap class loader", does not itself have a parent but may serve as the parent of a ClassLoader instance.

译:类加载器采用委派模型(即:双亲委派模型)来搜寻类或者资源。每一个类加载器实例都有一个与之关联的父亲加载器。当JVM要求子加载器去找某类或者资源时,子加载器会先委派给父亲加载器,直到顶层的根加载器(BootStrap加载器)
接上面的说,所有的加载任务都会先一级级的传到BootStrap,如果它没找到(类加载器会到自己负责的文件目录中寻找)它会告诉子加载器,子加载器才会开始尝试寻找。它的整个流程如下:


双亲委派机制

在ClassLoader.java中有三个重要方法,对于继承它的类加载器需要去实现:

Class findClass(String name)
Class<?> loadClass
final Class<?> defineClass(String name, byte[] b, int off, int len)

loadClass就是双亲委派的实际过程,我们不妨看看源码:

   protected Class<?> loadClass(String name, boolean resolve)
        throws ClassNotFoundException
    {
        synchronized (getClassLoadingLock(name)) {
            // 首先判断这个name的class是否加载过,如果加载过c!=null,直接返回
            Class<?> c = findLoadedClass(name);
            if (c == null) {
                //如果没有加载过这个类,则开始递归调用父加载器
                long t0 = System.nanoTime();
                try {
                    if (parent != null) {
                        c = parent.loadClass(name, false);
                    } else {
                        c = findBootstrapClassOrNull(name);
                    }
                } catch (ClassNotFoundException e) {
                    // ClassNotFoundException thrown if class not found
                    // from the non-null parent class loader
                }
                //此时c==null,表示父加载器也没有找到Class,这时候就自己找啰
                if (c == null) {
                    // If still not found, then invoke findClass in order
                    // to find the class.
                    long t1 = System.nanoTime();
                    //父加载器没找到Class,只能自己找,调用自己的findClass()
                    c = findClass(name);

                    // this is the defining class loader; record the stats
                    sun.misc.PerfCounter.getParentDelegationTime().addTime(t1 - t0);
                    sun.misc.PerfCounter.getFindClassTime().addElapsedTimeFrom(t1);
                    sun.misc.PerfCounter.getFindClasses().increment();
                }
            }
            if (resolve) {
                resolveClass(c);
            }
            return c;
        }
    }

可以看到双亲委派的流程是loadClass控制的,真实的去磁盘上找文件是由findClass()方法执行,但是如果看到findClass定义,你可以发现其中没有内容,这就是各个级别的类加载器需要自己复写的方法。我们在findClass中搜索文件你,如果找到文件,则通过defineClass将文件转化成输入流,进而读到内存方法区中,返回一个Class对象。

以上就是JVM对类加载流程的第一步:加载的完整过程及原理,通过类加载器,磁盘上的class文件就存到了内存方法区当中,下一步就是进行连接和初始化啦!

相关文章

  • 白话类加载

    无论你看哪个版本JVM书籍,类加载是绕不开的开篇第一课,然而我们对其理解往往受限于JVM繁复的概念,而无法真正消化...

  • JAVA-大白话探索JVM-类加载过程(二)

    首先我们知道JVM是什么以及类加载器的作用 不清楚的可以看看JAVA-大白话探索JVM-类加载器(一) 现在我们来...

  • 第一章 类加载过程

    要点 类加载过程 类加载器 一、类加载过程 1.类的加载过程 类的加载 .class文件过程分为:加载---->连...

  • 深入理解jvm类加载机制

    1.什么是类加载? 类加载机制一个很大的体系,包括类加载的时机,类加载器,类加载时机。 1.1类加载过程 加载器加...

  • java基础知识之java类加载器

    1. 什么是类加载器 类加载器就是用来加载类的东西!类加载器也是一个类:ClassLoader 类加载器可以被加载...

  • JVM类加载入门

    一 类加载顺序 class类加载-->验证-->准备--->解析--->初始化 class类加载:通过类加载器加载...

  • java类加载器及其原理

    java类加载器 : java中默认有三种类加载器:引导类加载器,扩展类加载器,系统类加载器(也叫应用类加载器) ...

  • 深入浅出“类加载器”

    内容概述 “类加载”介绍 “类加载器”介绍 深入“类加载器” 深入“父亲委托机制” 一,“类加载”介绍 “加载”是...

  • 《深入理解JVM虚拟机》读书笔记-类加载器&Java模块化系统

    类加载器 一.类加载器 1.1 类与类加载器 类加载器的定义: Java虚拟机设计团队有意把 类加载阶段中 的“ ...

  • 类加载机制(一)

    加载机制系列类加载机制(一)类加载机制(二)类加载机制(三) 类加载机制 1.JVM把class文件加载到内存,对...

网友评论

      本文标题:白话类加载

      本文链接:https://www.haomeiwen.com/subject/qlliictx.html