首页>>后端>>java->Class文件如何加载到JVM

Class文件如何加载到JVM

时间:2023-12-05 本站 点击:0

概述

上一篇可以不用掌握,但必须了解字节码文件的整体结构中提到了class文件其实就是二进制流,那么我们如何把二进制流加载到JVM中?今天来学一下类的加载

类加载基本分为3大步,loading(加载),linking(链接),initializing(初始化),linking又分为3步,分别是verification(验证),preparation(准备),resolution(解析)。基本流程如下图

Loading

定义:通过类的全限定名(路径+文件名)获取此类的二进制流(字节码文件),将二进制流转为方法区的运行时数据结构,同时创建class对象,作为此类的访问入口

加载工具:ClassLoader,有bootstrap,extention,app,custom加载器。

加载方式:parent delegating(双亲委派)

加载流程

(图片来源于网络)

流程简述

当加载类(假设为test)时,CustomClassLoader查询是否加载过test,是的话,直接返回,否则问父加载器AppClassLoader

AppClassLoader查询是否加载过test,是的话,直接返回,否则问父加载器ExtClassLoader

ExtClassLoader查询是否加载过test,是的话,直接返回,否则问父加载器BootstrapClassLoader

BootstrapClassLoader查询是否加载过test,是的话,直接返回,否则查看自己是否可以加载,是的话,加载并返回,否的话,则让ExtClassLoader去加载

ExtClassLoader查看自己是否可以加载,是的话,加载并返回,否的话,则让AppClassLoader去加载

AppClassLoader查看自己是否可以加载,是的话,加载并返回,否的话,则让CustomClassLoader去加载

CustomClassLoader查看自己是否可以加载,是的话,加载并返回,否的话,则抛出ClassNotFundException

BootstrapClassLoader:由C++实现,在Java中用null来指代,只加载最基础最重要的类,例如JRE的lib目录下jar包中的类以及有虚拟机参数-Xbootclasspath指定的类。

ExtClassLoader:其父加载器是BootstrapClassLoader,加载相对次要但通用的类,例如JRE的lib/ext目录下jar包的类,以及java.ext.dirs指定的类。(java 9 之后,改名为平台类加载器,加载更多类)

AppClassLoader:其父加载器是ExtClassLoader,加载应用程序路径下的类。

CustomClassLoader:自定义加载器,可实现特殊的加载方式,例如对class文件加密,加载时利用自定义的类加载器进行解密后加载。

双亲委派加载的源码

    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 {                        //最顶级父类bootstrapClassLoader加载                        c = findBootstrapClassOrNull(name);                    }                } catch (ClassNotFoundException e) {                    // ClassNotFoundException thrown if class not found                    // from the non-null parent class loader                }                //父类中既没有缓存,也无法加载,则当前classloader尝试加载                if (c == null) {                    // If still not found, then invoke findClass in order                    // to find the class.                    long t1 = System.nanoTime();                    //自定义classLoader只能重写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;        }    }

为什么需要用双亲委派? (常问面试题)

为了安全性,假设攻击者篡改系统级别的类,例如java.lang.String.Class,在未加载的情况下,会默认先轮询到bootstrapClassLoader,而bootStrapClassLoader已经加载过String.class了,并不会加载篡改后的class。

如何打破双亲委派?

自定义类加载器只要重新loadClass方法,即可以打破双亲委派。一般自定义类加载器只重新findClass方法是不会打破双亲委派的。详细介绍可以看此文:如何打破双亲委派

类的加载时机

jvm加载类采用的是懒加载,几种常见的场景:

new对象,getStatic,putStatic时

反射

初始化子类时

虚拟机启动时,主类必须加载

LINKING

verification

验证class文件的字节流符合虚拟机要求,保证被加载类的正确性。

文件格式验证:class文件开头都是CAFE BABE

元数据验证

字节码验证

符号引用验证

preparation

为静态变量分配内存,并将静态变量设置为初始值(零值)

resolution

将常量池内的符号引用转为内存中的直接引用

INITIALIZING

执行clinit方法,将执行所有静态变量和静态代码块的语句(会按照语句在原文件中的顺序进行执行),将静态变量设置为初始值。

原文:https://juejin.cn/post/7098264829915299847


本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。
如若转载,请注明出处:/java/12231.html