前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >类加载机制浅记

类加载机制浅记

原创
作者头像
猎户星座1
修改2020-07-30 09:53:29
3950
修改2020-07-30 09:53:29
举报
文章被收录于专栏:Java StudyJava StudyJava Study

Class 类加载机制,面试考察方面挺频繁的,今天在项目中也遇到了要了解类加载机制的地方,要了解Javaagent,Javaagent 大家知道它是在类加载时期对程序起到监控作用的。特此记录一下,发现真的以学习研究的项目为驱动,真的的会效率很高,而且执导老师说的多写demo ,现在养成一学知识就先想整个demo 出来,手头上的demo 还真的渐增了起来,今天看源码比网上的教程会明白,配合别人将源码的博客真的瞬间懂了点什么感觉。生看的化是很难记住的。

/A class loader is an object that is responsible for loading classes. The
* class <tt>ClassLoader</tt> is an abstract class.  Given the <a
* href="#name">binary name</a> of a class, a class loader should attempt to
* locate or generate data that constitutes a definition for the class.  A
* typical strategy is to transform the name into a file name and then read a
* "class file" of that name from a file system.
* 
* <p> Every {@link Class <tt>Class</tt>} object contains a {@link
* Class#getClassLoader() reference} to the <tt>ClassLoader</tt> that defined
*/ 

摘自 jdk 中 对class load 的介绍 第一次觉得 看注释这么有用,

一个class loader 是对 已加载到类响应的 object 他是一个抽象类,通过传递一个 二进制的类name ,类加载器试图定位或生成数据来构造一个对类的定义 典型的方式 是转化为一个 文件名称 然后读取类的名称在 文件系统中。 就是从文件系统中读取该文件名称的文件。

因此你可以通过 得到本类的class 对象 获得 加载本身的ClassLoader对象

ClassLoader classLoader = ClassLoaderDemo.class.getClassLoader();  

当然获得类加载的对象,并不是我们想了解的我们需要了解类加载的过程,才能在明白Javaagent 及一些在编译期发挥作用的字节码技术。关键有两个方法 loadClass 和 findClass 。

从这两个方法中可以引申出 类加载机制的很关键的一个 类加载模型 就是双亲委派机制模型。

类在加载时,首先会查看是否已经加载过了这个类,如果已经将类加载到 jvm 中 ,那就返回加载的类。

如果没有

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);
        //如果没有找到加载的类 返回为 null
        if (c == null) {
            //设置System.nanoTime()的返回值只和进程已运行的时间有关, 不受调系统时间影响. 后面计算时间有用的
            long t0 = System.nanoTime();
            try {
                //如果该类加载器有 父类 让父类尝试去加载 传入的name 类     也就是双亲委派的实现 让父类去先加载 
                if (parent != null) {
                    c = parent.loadClass(name, false);
                } else {
                //如果没有 则让 接下来这个   实际是这个 本地方法 c++ 去调用加载  private native Class<?> findBootstrapClass(String name);
                    c = findBootstrapClassOrNull(name);
                }
            } catch (ClassNotFoundException e) {
                 // 如果文件不存在 自然父类 加载不到 抛出异常
                // ClassNotFoundException thrown if class not found
                // from the non-null parent class loader
            }
            //如果父类返回的同样为null 说明父加载没有成功 
            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(c);
        }
        return c;
    }
}


    /
     * Finds the class with the specified <a href="#name">binary name</a>.
     * This method should be overridden by class loader implementations that      应该实现 其委派模型去加载类
     * follow the delegation model for loading classes, and will be invoked  该方法应该在执行了父类检查类加载后被执行
     * the {@link #loadClass <tt>loadClass</tt>} method after checking the    它默认只实现了抛出异常 
     * parent class loader for the requested class.  The default implementation
     * throws a <tt>ClassNotFoundException</tt>.
     *
     * @param  name
     *         The <a href="#name">binary name</a> of the class
     *
     * @return  The resulting <tt>Class</tt> object
     *
     * @throws  ClassNotFoundException
     *          If the class could not be found
     *
     * @since  1.2
     */





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

因为我们知道 所有的类加载器都要去继承 ClassLoader 去重写其方法,实现了的 类加载器,去重写findClass方法时要实现本 类加载器去加载传入的 name。

验证一下 查看一下 URLClassLoader 中 的 findClass() 方法

/
 * Finds and loads the class with the specified name from the URL search   找到 并加载类  
 * path. Any URLs referring to JAR files are loaded and opened as needed  
 * until the class is found.
 *
 * @param name the name of the class   
 * @return the resulting class
 * @exception ClassNotFoundException if the class could not be found,
 *            or if the loader is closed.
 * @exception NullPointerException if {@code name} is {@code null}.
 */
protected Class<?> findClass(final String name)
    throws ClassNotFoundException
{
    final Class<?> result;
    try {
        result = AccessController.doPrivileged(
            new PrivilegedExceptionAction<Class<?>>() {
                public Class<?> run() throws ClassNotFoundException {
                    String path = name.replace('.', '/').concat(".class");
                    Resource res = ucp.getResource(path, false);
                    if (res != null) {
                        try {
                            return defineClass(name, res);
                        } catch (IOException e) {
                            throw new ClassNotFoundException(name, e);
                        }
                    } else {
                        return null;
                    }
                }
            }, acc);
    } catch (java.security.PrivilegedActionException pae) {
        throw (ClassNotFoundException) pae.getException();
    }
    if (result == null) {
        throw new ClassNotFoundException(name);
    }
    return result;
}

证明它是继承了ClassLoader
证明它是继承了ClassLoader
网上经常出现的 类加载的图
网上经常出现的 类加载的图

最高层为 c++ 实现的类加载器。 下面两个为扩展的类加载器及 程序加载器, 是Launcher类中的 两个静态内部类。继承是上面所说的URLClassLoader的子类 网上一直拿上面的图举例说明肯定 这两个类肯定有作用。

static class AppClassLoader extends URLClassLoader {
static class ExtClassLoader extends URLClassLoader {

下次再去分析它

还有一个问题就是 为什么类要求 进行双亲委派制模型去进行类加载 ?

直接使用一个类加载器,或者根据文件的类型去划分类加载器去分别加载它不行吗?

只知道 bootstrap classloader 是使用 c++ 编写的 ,然后 下面两个是java 编写的,是不是很这样导致的分层关系,

网上的觉得有道理 摘抄过来

一个是避免重复加载,因为他会一只向上递归去检查自己的上一层父类是否已经被加载了。

下面说的意思归结起来就是 防止自定义的核心类库被破坏掉,我的想法:如果你自定义的类 和api里面定义的类 同时能够被类加载了,那jvm不知道执行哪个了,还有像下面说的 如果可以加载了自定义的超类 object ,那所有的类型都会按照你定义的object 去走,那肯定给你的开发 也带来不便和混乱。

不同层次的类加载器具有不同优先级,比如所有Java对象的超级父类java.lang.Object,位于rt.jar中,无论哪个类加载器加载该类,最终都是由启动类加载器进行加载,保证安全。即使用户自己编写一个java.lang.Object类并放入程序中,虽能正常编译,但不会被加载运行,因为Java中的java.lang.Object类是由启动类加载器进行加载,自己编写的java.lang.Object类不会被启动类加载器进行加载,保证不会出现混乱。相反,如果没有双亲委派模型而是由各个类加载器自行加载的话,如果用户编写了一个java.lang.Object的同名类并放在ClassPath中,那系统中将会出现多个不同的Object类,程序将混乱。

出处: https://www.cnblogs.com/wl889490/p/12782737.html

原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。

如有侵权,请联系 cloudcommunity@tencent.com 删除。

原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。

如有侵权,请联系 cloudcommunity@tencent.com 删除。

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档