跳至主要內容

JVM 类加载机制之 ClassLoader

JI,XIAOYONG...大约 3 分钟

本文为《一看你就懂,超详细 java 中的 ClassLoader 详解 - CSDN 博客》open in new window阅读笔记

简述

JVM 有三种类加载器:

  1. BootStrap ClassLoader 启动类加载器,加载核心类库,主要加载核心类库,%JRE_HOME%\lib 下的 rt.jar、resources.jar、charsets.jar 和 class 等。
  2. Extention ClassLoader 扩展类加载器,加载目录%JRE_HOME%\lib\ext 目录下的 jar 包和 class 文件。
  3. App ClassLoader 应用程序加载器,加载当前应用的 classpath 的所有类。

除以上三种外,还有用户自定义的类加载器。

每个类由加载它的类加载器和类本身确定其唯一性。也就是说,类加载器不同,类肯定不同。

加载过程

在加载类时,通过**“双亲委托”机制,依次从1** -> 3向上查询,再从3->1依次返回结果:

  1. 调用findLoadedClass(className) 查询是否已经加载该类
  2. 调用父加载器的loadClass(className,false),若父加载器为空,则调用BootStrap ClassLoader
  3. 如果还是没有加载到该类,调用findClass(className)

这样子保证了每个类都是先经过最顶端的类加载器BootStrap ClassLoader,如果没有加载到再依次经过Extention ClassLoaderApp ClassLoader 加载,确保如 String 等关键类不会被自定义的 ClassLoader 加载而导致异常。

protected Class<?> loadClass(String name, boolean resolve)
        throws ClassNotFoundException
    {
        synchronized (getClassLoadingLock(name)) {
            // 首先,检测是否已经加载
            Class<?> c = findLoadedClass(name);
            if (c == null) {
                long t0 = System.nanoTime();
                try {
                    if (parent != null) {
                        //父加载器不为空则调用父加载器的 loadClass
                        c = parent.loadClass(name, false);
                    } else {
                        //父加载器为空则调用 Bootstrap Classloader
                        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();
                    //父加载器没有找到,则调用 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()
                resolveClass(c);
            }
            return c;
        }
    }

AppClassLoaderExtClassLoader都继承自URLClassLoader

AppClassLoader的父加载器是ExtClassLoaderExtClassLoader的父加载器为null,故而会调用BootStrap ClassLoader

ClassLoader 如果没有指定父加载器,则默认的父加载器为AppClassLoader,自定义 ClassLoader 也是如此。

自定义 ClassLoader

自定义 ClassLoader 一般步骤:

  1. 继承自ClassLoader
  2. 重写findClass()
  3. findClass()方法中调用并返回defineClass()
class MClasLoader:ClassLoader(){

    override fun findClass(name: String?): Class<*> {

        var sysDir = System.getProperty("user.dir")
        var classPath = "$sysDir/src/main/res/$name.class"

        var classFile = File(classPath)
        //【注意】这里一次只读取一个字节,否则会报错 java.lang.ClassFormatError:
        // Extra bytes at the end of class file TestClass
        var bytes = ByteArray(1)
        var fileInputStream = FileInputStream(classFile)
        var len = -1
        var byteBuffer = ByteOutputStream()
        while (true) {

            len = fileInputStream.read(bytes)
            if (len > 0) {
                byteBuffer.write(bytes)
            } else {
                break
            }
        }
        var byteArr = byteBuffer.toByteArray()
        return defineClass(name,byteArr,0,byteArr.size)
    }
}

fun main(args: Array<String>) {

    var clazz = MClasLoader().loadClass("TestClass")
    var say = clazz.getDeclaredMethod("say",String::class.java)
    say.invoke(clazz.newInstance(),"hello world")

    print(MClasLoader().parent)
}

defineClass()则将一个字节数组转化为一个类的实例(Converts an array of bytes into an instance of class with an optional ProtectionDomain)

contextClassLoader

每个线程都有一个 ClassLoader:contextClassLoader,通过将其设置为自定义的 ClassLoader 可以在加载类的时候做一些特殊的事情。

    Thread.currentThread().contextClassLoader = MClasLoader()
    var clazz = Class.forName("TestClass",
            true, Thread.currentThread().contextClassLoader)
    var say = clazz.getDeclaredMethod("say",String::class.java)
    say.invoke(clazz.newInstance(),"hello world")

    println(Thread.currentThread().contextClassLoader )
    println(Thread.currentThread().contextClassLoader.parent )

结果为:

hello world
MClasLoader@1d44bcfa
sun.misc.Launcher$AppClassLoader@18b4aac2
文章标题:《JVM 类加载机制之 ClassLoader》
本文作者: JI,XIAOYONG
发布时间: 2018/04/15 16:40:32 UTC+8
更新时间: 2023/12/30 16:17:02 UTC+8
written by human, not by AI
本文地址: https://jixiaoyong.github.io/blog/posts/b3994218.html
版权声明: 本博客所有文章除特别声明外,均采用 CC BY-NC-SA 许可协议。转载请注明出处!
你认为这篇文章怎么样?
  • 0
  • 0
  • 0
  • 0
  • 0
  • 0
评论
  • 按正序
  • 按倒序
  • 按热度
Powered by Waline v2.15.8