Java类加载机制的理解

Wesley13
• 阅读 714

算上大学,尽管接触Java已经有4年时间并对基本的API算得上熟练应用,但是依旧觉得自己对于Java的特性依然是一知半解。要成为优秀的Java开发人员,需要深入了解Java平台的工作方式,其中类加载机制和JVM字节码这样的核心特性。今天我将记录一下我在新的学习路程中对Java类加载机制的理解。

1.类加载机制

类加载是一个将类合并到正在运行着的JVM进程中的过程。首先要加载一个类,我们必须先得将类文件加载进来并连接,并且要加上大量的验证,随后会生成一个代表着这个类的class对象,然后就可以通过它去创建新的实例了。这就是我所理解的Java的类加载机制。

经过加载和连接后出来的class对象,说明这个类已经被加载到了JVM中,此后就不会再加载了。

2.类加载器和双亲委派模型

Java平台里有几个经典的类加载器,它们分别在平台的启动和常规操作中承担不同的任务:

    根类加载器(Bootstrap ClassLoader)——通常在虚拟机启动不久后实例化,我们可以将其视作JVM的一部分,他的作用通常是负责加载系统的基础jar包(主要是rt.jar),并且它不做验证工作。我们开发者无法直接使用该加载器
    扩展类加载器(Extension ClassLoader)——用来加载安装时自带的标准扩展。一般包括安全性扩展。我们开发者可以直接使用。
    应用(或系统)类加载器(System ClassLoader)——这是应用最广泛的类加载器。它负责加载应用类。
    定制类加载器(Custom ClassLoader)——在更复杂的环境中,我们开发者可以通过继承java.lang.ClassLoader类的方式实现自己的类加载器以满足一些特殊的需求。比如说Spring框架里的封装了自己的类加载器(我会在随后的spring源码学习中遇到的吧)
    线程上下文类加载器(Context ClassLoader)——默认为系统类加载器,可通过Thread.currentThread().setContextClassLoader(ClassLoader)来设置,每个线程都可以将线程上下文类加载器预先设置为父线程的类加载器。这个主要用于打破双亲委派模型,容许父类加载器通过子类加载器加载所需要的类库。

说到双亲委派模型,我们可以通过这个图可知:

Java类加载机制的理解
如果说一个类加载器收到类加载请求,它并不会马上去找,它会先把这个请求委托给他的父类加载器去完成,只有它的父类加载器反馈说找不到了,它才会自己去找(注:父类加载器它们的默认的目录路径都是不一样的,一个类在虚拟机里面是用它的全限定类名+它的类加载器来确立它的唯一性),采用双亲委派的好处可以保证系统中的类不混乱,如你自己写了一个java.lang.object类,并且路径也放在lib下面,此时编译后会有两个object类了,采用双亲委派就只会加载rt.jar里面的object而不加载你自己写的。

3.加载类

手动加载类有两种方式,Class.forName()和ClassLoader.loadClass()两种:

我们从源码来看看他们的区别

Class.forName()

它有两个重载方法

   

    public static Class forName(String className)
                    throws ClassNotFoundException {
            return forName0(className, true, ClassLoader.getCallerClassLoader());
        }

    public static Class forName(String name, boolean initialize,
                                   ClassLoader loader)
        throws ClassNotFoundException
    {
        if (loader == null) {
            SecurityManager sm = System.getSecurityManager();
            if (sm != null) {
                ClassLoader ccl = ClassLoader.getCallerClassLoader();
                if (ccl != null) {
                    sm.checkPermission(
                        SecurityConstants.GET_CLASSLOADER_PERMISSION);
                }
            }
        }
        return forName0(name, initialize, loader);
    }

        /** Called after security checks have been made. */
        private static native Class forName0(String name, boolean initialize,
                                                ClassLoader loader)
            throws ClassNotFoundException;

第一个方法默认初始化类。

第二个方法可以选择是否初始化类和可以选择类加载器。

它们俩最终都是返回forName0,而forName0有native关键字,原生态的方法,是其他语言实现的,我就不深究下去了。

ClassLoader.loadClass()

它同样也有两个重载方法

        public Class loadClass(String name) throws ClassNotFoundException {
            return loadClass(name, 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;
            }
        }

第一个方法的具体实现是第二个方法。resolve参数为false。

第二个方法先检查这个类是否被加载过,如果没有被加载过,那就通过双亲委派的模式去找,如果父类加载器不是根类加载器,那就递归调用,如果是,那就试着返回根类加载器所加载的类或者返回null,当虚拟机想要装入的不仅包括指定的类似,resolve参数为true,装入该类英语的所有其他类。这个方法还用了其他的类,它们基本都是用native修饰的,在这也不深究,知道是用来干嘛的就好了。

以上就是我所学习的关于Java类加载器的内容。

https://blog.csdn.net/donggua3694857/article/details/51932630

点赞
收藏
评论区
推荐文章
Wesley13 Wesley13
3年前
java反序列化——apache
看了好久的文章才开始分析调试java的cc链,这个链算是java反序列化漏洞里的基础了。分析调试的shiro也是直接使用了cc链。首先先了解一些java的反射机制。一、什么是反射反射是Java的特征之一,是一种间接操作目标对象的机制,核心是JVM在运行的时候才动态加载类,并且对于任意一个类,都能够知道这个类的所有属性和方法,调用方法/访问属性
Wesley13 Wesley13
3年前
java 面试知识点笔记(一)底层知识 jvm相关
问:谈谈你对java的理解?平台无关性(一处编译多处运行)GC(垃圾回收机制)语言特性(泛型、反射、lambda表达式)面向对象(封装继承多态)类库(集合、网络库、并发库、nio)异常处理javac将.java文件进行编译,编译出.class文件(二进制字节码,包含java类中的属性
Wesley13 Wesley13
3年前
Java高级篇——深入浅出Java类加载机制
类加载器简单讲,类加载器ClassLoader的功能就是负责将class文件加载到jvm内存。类加载器分类从虚拟机层面讲分为两大类型的类加载器,一是BootstrapClassloader即启动类加载器(C实现),它是虚拟机的一部分,二是其他类型类加载器(JAVA实现),在虚拟机外部,并全部继
Stella981 Stella981
3年前
Jvm类的加载机制
1.概述虚拟机加载Class文件(二进制字节流)到内存,并对数据进行校验、转换解析和初始化,最终形成可被虚拟机直接使用的Java类型,这一系列过程就是类的加载机制。2.类的加载时机类从被虚拟机加载到内存开始,直到卸载出内存为止,整个生命周期包括:加载——验证——准备——解析——初始化——使用——卸载这7个阶段。其中验
Wesley13 Wesley13
3年前
Java类加载机制
启动(Bootstrap)类加载器启动类加载器主要加载的是JVM自身需要的类,这个类加载使用C语言实现的,是虚拟机自身的一部分,它负责将<JAVA\_HOME/lib路径下的核心类库或Xbootclasspath参数指定的路径下的jar包加载到内存中,注意必由于虚拟机是按照文件名识别加载jar包的,如rt.jar,如果文件名不被虚拟机
Wesley13 Wesley13
3年前
Java虚拟机类加载机制
概述  虚拟机把描述类的数据从Class文件加载到内存,并对数据进行校验、转换解析和初始化,最终形成可以被虚拟机直接使用的Java类型,这就是虚拟机的类加载机制。  与那些在编译时需要进行连接工作的语言不同,在Java语言里面,类型的加载、连接和初始化过程都在程序运行期间完成的,这种策略虽然会稍微增加一些系统性能开销,但是会为Java应用程序
Wesley13 Wesley13
3年前
Java反射技术概述
1.什么是Java反射?  就是正在运行,动态获取这个类的所有信息2.反射机制的作用  a.反编译:.class.java  b.通过反射机制,访问Java对象的属性,方法,构造方法等3.反射机制的应用场景  Jdbc加载驱动  SpringIOC实现  Java框架4.创建对象的两种方式  a.直
Stella981 Stella981
3年前
EKT Java企业级关键技术强化 Enterprise Edition
EKTenterprisekeytechlology企业关键技术本章目标:1.理解Class类2.理解JAVA类加载体系结构3.理解类的加载过程Class对象由JVM自动产生,每当一个类被加载时,JVM就自动为其生成一个Class对象,通过Class对象可以获得类的相关信息。将类信息读到内存中过程,称为类加载
Java类加载机制详解 | 京东云技术团队
一.类加载器及双亲委派机制|类加载器|加载类|备注||||||启动类加载器(BootstrapClassLoader)|JAVAHOME/jre/lib|无上级,无法直接访问由jvm加载||拓展类加载器(ExtensionClassLoader)|JAVA