美文网首页
类加载的过程

类加载的过程

作者: 掩流年 | 来源:发表于2019-12-31 13:53 被阅读0次

类从编译到执行的过程

类从编译到执行的过程主要经历了以下几个步骤:
例如加载ClassLoadTest.java文件
1.编译器将ClassLoadTest.java源文件编译成ClassLoadTest.class字节码文件。
2.ClassLoader将字节码文件转换为JVM中的Class<ClassLoadTest>对象。
3.JVM利用Class<ClassLoadTest>对象实例化ClassLoadTest对象。

看一个加载的例子

Class aClass = ClassLoadTest.class; 

ClassLoader classLoader = ClassLoadTest.class.getClassLoader();
Class class3 = classLoader.loadClass("com.reflect.ClassLoadTest");

如之上代码所示,其实本质上是等价的。在java虚拟机规范中讲明了这一点。

If D was defined by a user-defined class loader, then that same user-defined class loader initiates loading of C (§5.3.2).

其中涉及到两个名词。我们称触发一个类加载的过程的类加载器叫做initiating loader,而最终加载这个类的类加载器叫做defining loader。例子:一个类加载器X触发了java.util.Map类的加载,但是它最终被系统的类加载器加载了(因为双亲委派模型),我们称X为initiating loader,系统的类加载器为defining loader。

如果使用Idea中的ASM插件去查看字节码的话,会发现Class aClass = ClassLoadTest.class;这段代码执行了LDC指令,会去常量池中拿东西。我们假设这段代码是运行在Test类中的,这时候,ClassLoadTest.class的加载会被Test.class.classLoader作为initiating loader触发。
ClassLoader classLoader = A.class.getClassLoader();其中的classLoader就是最终加载A.class的类加载器。
我们令classLoader.loadClass("com.ClassLoadTest");就是令该类加载器加载ClassLoadTest.class

什么是ClassLoader

ClassLoader是Java中的核心组件,它主要是从系统外部读取class文件字节码,加载进JVM中,交由JVM进行连接初始化等。

ClassLoader的种类

  • BootStrapClassLoader:C++编写的,加载核心库*.java
  • ExtClassLoader:Java编写,加载扩展库 javax.*
  • AppClassLoader:Java编写,加载程序所在的目录。
  • 自定义ClassLoader:Java编写,定制化加载
    所以自定义ClassLoader指的是自己定义编写类加载的规则,如何自己编写一个ClassLoader呢?
    java.lang.ClassLoader这个抽象类中,有一个方法:
    protected Class<? > findClass(String name) throws ClassNotFoundException {
        throw new ClassNotFoundException(name);
    }

它的定义直接抛出异常,它的作用是提供给用户覆盖方法,编写自定义的ClassLoader。自定义编写ClassLoader的代码如下:

public class MyClassLoader extends ClassLoader {

    private String fileName;
    private String path;

    public MyClassLoader(String fileName, String path) {
        this.fileName = fileName;
        this.path = path;
    }

    @Override
    public Class findClass(String name) {
        byte[] b = new byte[0];
        try {
            b = loadClassData(name, path);
        } catch (IOException e) {
            e.printStackTrace();
        }
        return defineClass(name, b, 0, b.length);
    }

    private byte[] loadClassData(String name, String path) throws IOException {
        return Files.readAllBytes(Paths.get(path + name + ".class"));

    }

}

这样就完成了一个类加载器的编写。我们用写一个检查类进行验证

class Check{
    public static void main(String[] args) throws ClassNotFoundException, IllegalAccessException,
            InstantiationException {
        MyClassLoader myClassLoader = new MyClassLoader("LoadFile","\\mypath");
        Class c = myClassLoader.loadClass("LoadFile");
        System.out.println(c.getClassLoader());
        c.newInstance();
    }
}

打印的结果为

com.reflect.MyClassLoader@7d4991ad
load file ...

类加载器的双亲委派机制

双亲委派机制

如上图所示,在开始类加载之前,类加载器首先会检查这个类曾经有没有被加载过。加载过则直接使用加载的Class对象,没有加载则向上委托给它的父加载器,检查父加载器有没有加载过。
如果都没有加载过这个类,则会从上到下检查classpath中的jar,查找包中的jar是否有对应的类。
使用双亲委派机制来加载类,主要是避免多份同样字节码的加载。

loadClass和forName的区别

类的装载过程主要有以下三步:

  • 加载
    通过ClassLoader加载class文件字节码,生成Class对象。
  • 链接
    1.校验:检查加载class的正确性和安全性
    2.准备:为类变量分配存储空间并设置类变量初始值
    3.解析:JVM将常量池的符号引用转换为直接引用
  • 初始化
    执行类变量赋值和静态代码块

对于Class.forName和ClassLoad.loadClass而言,主要的区别是:
Class.forName得到的class是完成初始化的。 ClassLoad.loadClass得到的class是没有链接的,也就是没有初始化的。
这点可以在源代码中体现:
forName的代码,可以看到它的返回方法中第二个参数置为true,则表明会被初始化。

    @CallerSensitive
    public static Class<?> forName(String className)
                throws ClassNotFoundException {
        Class<?> caller = Reflection.getCallerClass();
        return forName0(className, true, ClassLoader.getClassLoader(caller), caller);
    }

对于loadClass而言,它最后执行的方法如下,它的初始化默认是为false的。

    protected Class<?> loadClass(String name, boolean resolve)
        throws ClassNotFoundException
    {
        synchronized (getClassLoadingLock(name)) {
            // First, check if the class has already been loaded
            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
                }

                if (c == null) {
                    // If still not found, then invoke findClass in order
                    // to find the class.
                    long t1 = System.nanoTime();
                    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;
        }
    }

由于它的方法是protected修饰的,所以子类无法继承。但是我们有反射呀...在Java8中,有了反射就可以随时随地的扒下JVM的内裤了。。。

相关文章

  • 第一章 类加载过程

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

  • 2019年JVM面试都问了什么?快看看这22道面试题!(附答案解

    一. Java 类加载过程? Java 类加载需要经历一下 7 个过程: 1. 加载 加载是类加载的第一个过程,在...

  • 面经题目学习

    java类加载过程类加载过程:加载、验证、准备、解析、初始化。类加载过程中主要是将Class文件(类的二进制字节流...

  • jvm相关面试题

    类加载的过程 classLoader.loadClass的类加载过程有如下几步:加载 >> 验证 >> 准备 >>...

  • JVM总括四-类加载过程、双亲委派模型、对象实例化过程

    JVM总括四-类加载过程、双亲委派模型、对象实例化过程 目录:JVM总括:目录 一、 类加载过程 类加载过程就是将...

  • JVM-类加载器

    类加载器 类加载器是用来把类加载到JAVA虚拟机中。 类加载的过程 类的加载过程采用了父亲委托的机制,这种机制能更...

  • 01-类加载过程

    类加载过程 类加载可以分为加载、连接、初始化3个部分 加载 加载过程是指查找并加载类的二进制数据,加载class文...

  • 类加载过程--加载

    类从加载到虚拟机内存中开始,到卸载出内存为止的步骤如下:http://www.jianshu.com/p/66ec...

  • 深入理解jvm类加载机制

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

  • Java类加载问题

    类加载过程类加载过程:加载->连接->初始化。连接过程又可分为三步:验证->准备->解析。一个非数组类的加载阶段(...

网友评论

      本文标题:类加载的过程

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