4.深入理解java虚拟机-类加载器深入

1.类加载器:通过一个类的全限定名获取类的二级制字节流的这部分工作放在外部,由程序来决定 -这块代码模块叫类加载器
类加载器的树状结构:
          

a.引导类加载器(bootstrap class loader)-根目录的类加载器,最顶级的类加载器,用来加载
    java代码的核心类库,是用原生代码实现的,不继承子java.lang.classLoader
b.扩展类加载器(extensions class loader)-加载扩展类库
c.系统类加载器(system class loader)根据java引用的类路径classpath来加载类,一眼来说都
    是继承自java.lang.classLoader,可以通过classLoader.getSystemLoader方法来获取


            除去上面的,我们可以通过继承java.lang.classLoader类来实现自己的类加载器
         类加载器的树状图
2.类加载器的代理模式:类加载器在加载时候,会一直委托父加载器去加载,直到没有父加载器为止;比较任意两个类相等,是由他是否是同一个类加载器加载和这个类本身一起定义的;
即使是由一个类加载器的不同实例加载同一个类,这样的加载出来的两个类也是不相等的,假如要使用会报错:
    

classCastException
            
            demo:
            package com.example; 
 
            public class Sample { 
               private Sample instance; 
             
               public void setSample(Object instance) { 
                   this.instance = (Sample) instance; 
               } 
            }
            
            
            public void testClassIdentity() { 
               String classDataRootPath = "C:\\workspace\\Classloader\\classData"; 
               FileSystemClassLoader fscl1 = new FileSystemClassLoader(classDataRootPath); 
               FileSystemClassLoader fscl2 = new FileSystemClassLoader(classDataRootPath); 
               String className = "com.example.Sample";    
               try { 
                   Class<?> class1 = fscl1.loadClass(className); 
                   Object obj1 = class1.newInstance(); 
                   Class<?> class2 = fscl2.loadClass(className); 
                   Object obj2 = class2.newInstance(); 
                   Method setSampleMethod = class1.getMethod("setSample", java.lang.Object.class); 
                   setSampleMethod.invoke(obj1, obj2); 
               } catch (Exception e) { 
                   e.printStackTrace(); 
               } 
            }
                


        由此,可以知道代理模式的设计动机:代理模式的设计动机了。代理模式是为了保证 Java 核心库的类型安全。
 所有 Java 应用都至少需要引用 java.lang.Object类,也就是说在运行的时候,java.lang.Object这个类需要被加载到 Java 虚拟机中。如果这个加载过程由 Java 应用自己的类加载器来完成的话,很可能就存在多个版本的 java.lang.Object类,而且这些类之间是不兼容的。通过代理模式,对于 Java 核心库的类的加载工作由引导类加载器来统一完成, 保证了 Java 应用所使用的都是同一个版本的 Java 核心库的类,是互相兼容的。

3.全局负责机制:

“全盘负责”是指当一个ClassLoader装载一个类时,除非显示地使用另一个ClassLoader,则该类所依赖及引用的类也由这个CladdLoader载入。

例如,系统类加载器AppClassLoader加载入口类(含有main方法的类)时,会把main方法所依赖的类及引用的类也载入,依此类推。“全盘负责”机制也可称为当前类加载器负责机制。显然,入口类所依赖的类及引用的类的当前类加载器就是入口类的类加载器。



4..破坏双亲委派模型的方法:
    线程上下文加载器类:线程上下文类加载器(context class loader)是从 JDK 1.2 开始引入的。类 java.lang.Thread中的方法 getContextClassLoader()和 setContextClassLoader(ClassLoader cl) 用来获取和设置线程的上下文类加载器。如果没有通过 setContextClassLoader(ClassLoader cl)方法进行设置的话,线程将继承其父线程的上下文类加载器。Java 应用运行的初始线程的上下文类加载器是系统类加载器。在线程中运行的代码可以通过此类加载器来加载类和资源。
       举例:服务类接口:java提供核心接口定义,属于核心库中,但是具体实现依靠各自的实现,核心库是引导类加载器,这些实现是系统类加载器,引导类加载器无法加载到这些由系统类加载器加载的类, 因此,使用上下文加载器类,不设置setContextClassLoader方法,便可以借上下文加载器类来实现加载的问题
            
            
5.关于类加载器代码分析:类加载器的加载过程
      

 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) {
                        //如果当前类没有被加载且父类加载器不为null,则请求父类加载器进行加载操作
                        c = parent.loadClass(name, false);
                    } else {
                       //如果当前类没有被加载且父类加载器为null,说明此时已经到了根加载器了,则请求根类加载器进行加载操作
                        c = findBootstrapClassOrNull(name);
                    }
                } catch (ClassNotFoundException e) {
                }

                if (c == null) {
                    long t1 = System.nanoTime();
                    //如果父类加载器加载失败,则由当前类加载器进行加载,
                    c = findClass(name);

                    //进行一些统计操作
                    sun.misc.PerfCounter.getParentDelegationTime().addTime(t1 - t0);
                    sun.misc.PerfCounter.getFindClassTime().addElapsedTimeFrom(t1);
                    sun.misc.PerfCounter.getFindClasses().increment();
                }
            }
            //初始化该类
            if (resolve) {
                resolveClass(c);
            }
            return c;
        }
    }


    
6.getClassLoader方法返回的都是系统类加载器的实例
    
7.class.forName加载和classLoader加载的区别:
     总体:class.forName底层就是Class.forName(className,true,classloader),代表加载完后需要初始化数据;
     再看ClassLoader.loadClass(className)方法,其实他调用的方法是ClassLoader.loadClass(className,false);还是注意看第2个 boolean参数,该参数表示目标对象被装载后不进行链接,这就意味这不会去执行该类静态块中间的内容。因此2者的区别就显而易见了。

参考:class.forName和classLoader加载的区别

8.关于双亲委派:解疑(重要)

双亲委派模型的工作过程:如果一个类加载器收到了类加载的请求,它首先不会自己去尝试加载这个类,而是把这个请求委派给父类加载器去完成,每一个层次的类加载器都是如此,因此所有的加载请求最终都应该传送到顶层的启动类加载器中,只有当父加载器反馈自己无法完成这个加载请求(它的搜索范围中没有找到所需的类)时,子加载器才会尝试自己去加载。

本质上最终都会走到根加载器,根加载器使用findBootstrapClassOrNull这个方法,发现加载不了,就会返回null,接着调用加载当前类的加载器中实现的findClass方法去处理类

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();                 
}


链接:https://juejin.im/post/5b396980e51d4562aa017964

 
参考博客:

    IBM的,比较深
    掘金-探秘类加载和类加载机制
    简书:类加载器介绍:双亲委派等解释  简单易懂

   知乎文章

比较全面的一篇文章

更棒的知乎好文,提问式介绍类加载器:知乎:让人怕怕的类加载器

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值