zoukankan      html  css  js  c++  java
  • java类加载机制

    类加载

    java类加载器

    Java中的类加载器,有启动类加载器(Bootstrap Classloader)、扩展类加载器(Launcher(ExtClassLoader)、应用程序类加载器(Launcher)AppClassLoader),用户还可以实现自定义的类加载器。

    • Bootstrap类加载器负责加载rt.jar中的JDK类文件,它是所有类加载器的父加载器。默认是负责加载$JRE_HOME/lib目录下面的类,也可以通过JVM参数-Xbootclasspath来指定需要加载类的路径.

    • Extension将加载类的请求先委托给它的父加载器,也就是Bootstrap,如果没有成功加载的话,再从jre/lib/ext目录下或者java.ext.dirs系统属性定义的目录下加载类。Extension加载器由sun.misc.Launcher$ExtClassLoader实现。

    • Application类加载器, 它负责从classpath环境变量中加载某些应用相关的类,classpath环境变量通常由-classpath或-cp命令行选项来定义,或者是JAR中的Manifest的classpath属性。Application类加载器是Extension类加载器的子加载器。通过sun.misc.Launcher$AppClassLoader实现。

    Bootstrap类加载器是由C来写的,其他的类加载器都是通过java.lang.ClassLoader来实现的。

    jvm启动类加载器加载过程:

    1. 根据JVM内存配置要求,为JVM申请特定大小的内存空间;
    2. 创建一个引导类加载器(Bootstrap Classloader)实例,初步加载系统类到内存方法区区域中;
    3. 创建JVM 启动器实例 Launcher,并取得类加载器ClassLoader(在Launcher的内部,其定义了两个类加载器,分别是sun.misc.Launcher.ExtClassLoader和sun.misc.Launcher.AppClassLoader);
    4. 使用上述获取的ClassLoader实例加载我们定义的Main类;
    5. 加载完成时候JVM会执行Main类的main方法入口,执行Main类的main方法;
    6. 结束,java程序运行结束,JVM销毁。

    ClassLoader源码解析

    1. loadClass

    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); // 检查该类是否已经被JVM加载过,若加载过则直接返回
            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;
        }
    }
    

    由loadClass源码可知, loadCLass加载步骤为:

    1. 先从jvm中找, 若之前已经被加载则返回对应Class, 若为之前未被加载则走2
    2. 若此类加载器存在父类, 则交给父类加载, 若父类加载成功返回类对象, 加载失败返回null则走4
    3. 若此类加载器不存在父类, 则交给Bootstrap Classloader加载, 若加载成功返回类对象, 加载失败返回null则走4
    4. 上述加载失败, 调用当前类加载器的findClass加载, 加载成功返回类对象(加载失败后...).

    由loadClass的逻辑已经能够清楚的看出双亲委派原则的实现逻辑:

    首先都是交给父类去加载,如果父类无法加载再交给子类去完成,直到调用用户自定义的类加载器去加载,如果全部都无法加载,就会抛出ClassNotFoundException。

    2. findClass

    protected Class<?> findClass(String name) throws ClassNotFoundException {
            throw new ClassNotFoundException(name);
    }
    

    CLassLoader中的findClass交由子类实现。

    3. defineClass

    通过字节码byte[] 获取字节码对象。

    DefineClass在Java反序列化漏洞当中的利用

    protected final Class<?> defineClass(String name, byte[] b, int off, int len)
            throws ClassFormatError {
            return defineClass(name, b, off, len, null);
        }
     protected final Class<?> defineClass(String name, byte[] b, int off, int len,
                                             ProtectionDomain protectionDomain)
            throws ClassFormatError{
            protectionDomain = preDefineClass(name, protectionDomain);
    
            Class c = null;
            String source = defineClassSourceLocation(protectionDomain);
    
            try {
                c = defineClass1(name, b, off, len, protectionDomain, source);
            } catch (ClassFormatError cfe) {
                c = defineTransformedClass(name, b, off, len, protectionDomain, cfe,
                                           source);
            }
    
            postDefineClass(c, protectionDomain);
            return c;
        }
    

    参考
    JVM类加载器机制与类加载过程

    DefineClass在Java反序列化漏洞当中的利用

  • 相关阅读:
    spring中的异步事件
    spring中的事件 applicationevent 讲的确实不错
    freemark2pdf
    使用Ajax生成的Excel文件并下載
    1.智帮校园App功能概况
    MVC4 Controller器同名问题
    MVC 数据验证
    补丁惹的祸-ContractName Microsoft.VisualStudio.Text.ITextDocumentFactoryService
    .Net MVC4 加.html后缀报404问题
    VS2012常用快捷建(必备)
  • 原文地址:https://www.cnblogs.com/jxkun/p/9354708.html
Copyright © 2011-2022 走看看