当前位置: 代码迷 >> Java相关 >> 读《深入理解Java虚拟机》随感——第二部分:虚拟机类加载机制
  详细解决方案

读《深入理解Java虚拟机》随感——第二部分:虚拟机类加载机制

热度:72   发布时间:2016-04-22 19:08:18.0
读《深入理解Java虚拟机》有感——第二部分:虚拟机类加载机制
一、类加载过程
      执行时机:编译程序——>执行程序(JVM启动、程序运行),类加载发生在程序运行期间

      各个阶段:分为加载阶段、连接阶段(验证、准备、解析)、初始化、使用、卸载

      执行顺序:大体是按以上阶段依次执行,但相互间有交叉
 
                     加载——>验证(文件格式)——>继续加载——>解析——>验证(元数据、字节码)——>准备——>初始化

      参与角色:Class文件、Java虚拟机、类加载器      
                   /**HotSpot的Bootstrap ClassLoader(启动类加载器)是位于虚拟机内部(由C++实现),其它类加载器外置于JVM(由Java实现)*/
 
二、说明—各个阶段
      加载阶段:
           普通类/接口、 数组类(组件类型为基本类型,如int[][]):获取、转化、创建、触发
                   获取——类加载器加载Class文件(指定路径+文件名 ——>确定“全限定名称”——>拿到Class文件(与平台无关的二进制字节流))
                   转化——字节码以一定转化成格式,存放在方法区
                   创建——方法区中生成一个代表这个类的java.lang.Class对象
                   触发——加载的同时,会触发父类、关联引用类的加载
                                             class A extends B————>Class<B>
                               private Class<Person> class————>Class<Person> 
                               Class c =Person.getClass(); ————>Class<Person>                             
                                       
1.关于“类加载器”和“生成的这个Class对象”:
1)类加载器
Bootstrap Loader(启动类加载器)、Extended Loader(标准扩展类加载器ExtClassLoader)、AppClass Loader(系统类加载器/应用程序类加载器AppClassLoader)
启动类加载器:
目的:加载java_home\lib目录下的字节码文件(如:rt.jar,含有java.lang.Object等基本类) 具体有哪些文件及加载顺序?
方式:加载System.getProperty("sun.boot.class.path")所指定的路径或jar,在使用Java运行程序时,也可以指定其搜索路径,例如:java -Djava.ext.dirs=d:\projects\testproj\classes HelloWorld 参考:http://www.cnblogs.com/ITtangtang/p/3978102.htm
               标准扩展类加载器:
目的:
加载java_home\lib\ext目录下的字节码文件
                         方式:加载System.getProperty("java.ext.dirs")所指定的路径或jar。在使用Java运行程序时,也可以指定其搜索路径,例如:java -Djava.ext.dirs=d:\projects\testproj\classes HelloWorld
结果:<实现类>sun.misc.Luncher@ExtClassLoader————————<继承关系>ClassLoader>URLClassLoader>AppClassLoader


               应用程序类加载器:
方式:加载System.getProperty("java.class.path")所指定的路径或jar。在使用Java运行程序时,也可以加上-cp来覆盖原有的Classpath设置,例如: java -cp ./lavasoft/classesHelloWorld
结果:<实现类>sun.misc.Luncher@AppClassLoader————————<继承关系>ClassLoader>URLClassLoader>AppClassLoader

自定义类加载器:
方式:1)继承java.lang.ClassLoader并重写loadClass方法;2)继承java.lang.ClassLoader并重写findClass方法/**JDK1.2后推荐,原因见下方红色部分*/
相关:
 1 <一>java.lang.Object 2 1.getClass() 3 public final native Class<?> getClass();    //拿到运行时的Class对象【通过本地方法】 4 /** 5  *例子:class-Test>main>  6  *                    Class c =Person.getClass(); 7  */ 8      /** 9        *实现:  10        *1)虚拟机启动11        *2)虚拟机类加载——Test.class————加载阶段:方法区>外部接口>new java.lang.Class  //Class<?>     12        *3)虚拟机类加载——java.lang.Object————【Test加载阶段】触发13        *3)虚拟机类加载——Person.class    ————【Test加载阶段】触发14        *4)应用程序启动15        *5)调用java.lang.ClassLoader>xxx1()、xxx2().....——返回运行时<Person>Class对象16        */17 18 19 <二>java。lang.ClassLoader20       1.loadClass(String name, boolean resolve)  /**加载指定名称(包括包名)的二进制类型,同时指定是否解析*/21         loadClass(String name)   22     protected Class<?> findClass(String name) throws ClassNotFoundException {   //空方法23         throw new ClassNotFoundException(name);24     }25     protected Class<?> loadClass(String name, boolean resolve)    //拿到类加载器【通过本地方法】26         throws ClassNotFoundException27     {28         synchronized (getClassLoadingLock(name)) {29             // First, check if the class has already been loaded30             Class c = findLoadedClass(name);31             if (c == null) {32                 long t0 = System.nanoTime();33                 try {34                     if (parent != null) {35                         c = parent.loadClass(name, false);36                     } else {37                         c = findBootstrapClassOrNull(name);38                     }39                 } catch (ClassNotFoundException e) {40                     // ClassNotFoundException thrown if class not found41                     // from the non-null parent class loader42                 }43 44                 if (c == null) {45                     // If still not found, then invoke findClass in order46                     // to find the class.47                     long t1 = System.nanoTime();48                     c = findClass(name);49 50                     // this is the defining class loader; record the stats51                     sun.misc.PerfCounter.getParentDelegationTime().addTime(t1 - t0);52                     sun.misc.PerfCounter.getFindClassTime().addElapsedTimeFrom(t1);53                     sun.misc.PerfCounter.getFindClasses().increment();54                 }55             }56             if (resolve) {57                 resolveClass(c);58             }59             return c;60         }61     }62     63     64     65     66 <三>java.lang.Class   67      1.getClassLoader()68 /**例子:class-Test>main>69   *                    Object o =new Object();70   *                       System.out.println(o.getClass().getClassLoader());71                                                                    结果>java.lang.NullPointerException   72   */                          73      /**74       *Returns the class loader for the class.  75       *This method will return null in such implementations if this class was loaded by the bootstrap class loader. 76       *如果没有指定classLoader就默认返回bootstrap classLoader(启动类加载器),因为这个bootstrap classLoader77       *用户拿不到实例,所以返回null表示返回的是bootstrap classLoader     78       */79 native ClassLoader getClassLoader0();      //拿到类加载器【通过本地方法】80 public ClassLoader getClassLoader() {81         ClassLoader cl = getClassLoader0();82         if (cl == null)83             return null;84         SecurityManager sm = System.getSecurityManager();85         if (sm != null) {86             ClassLoader.checkClassLoaderPermission(cl, Reflection.getCallerClass());87         }88         return cl;89     }
2.指定类加载器加载类
Class.forName(name, initialize, loader)

 

       2)与“生成的Class对象”关系
同一Class文件只有被同一类加载器加载,才能判断为相等————>相等的意义在于:1)不相等,会生成多个Class对象;相等,只会生成一个Class对象
2) 只有同一个Class对象,
equals()、isAssignabaleFrom()、instanceof、isInstance()返回结果才相同;
       3)双亲委派模型 

关系?
上一级持有下一级的一个引用,属于 has A关系
分工?(为什么叫双亲委派)
一个类加载器收到了类加载的请求,它首先不会自己去尝试加载这个类,而是把这个请求委派给上一层的类加载器去完成。只有当上级加载器在搜索范围找不到所需类,无法完成这个加载请求时,下级加载器才会尝试自己去加载
好处?
Java类随着它的类加载器一起具备了一种优先级的层次关系


2.加载“类/接口”的策略:
非数组类/接口、数组类(组件类型为基本类型,如int[][]):
加载生成代表这个类的java.lang.Class对象后,将在类加载器的“类名称空间”上标识为“已加载”(因为前面已经讨论了,
同一Class文件对应同一个类加载器才确定生成的是同一个Class对象)
           数组类(组件类型为引用类型,如Person[][])、非数组类/接口:
                 递归加载组件类型,每次去掉一个维度

 

      连接阶段

            验证——文件格式、元数据、字节码

            准备——在方法区为类变量分配内存并初始化

                               例子                     编译时期                                                             (类加载时)验证阶段—准备时期

                static int i =20;             产生constantValue属性,但不会存入常量20                       常规方式进行准备:初始化、分配内存

                             结果—————>        无                                                                                    0                                                                                            

         final static int i =20;             产生constantValue属性,并存20到constantValue               从对应constantValue取出来初始化

                             结果—————>       20                                                                                   20

            解析——将运行时常量池的符号引用替换为直接引用(指向内存目标的句柄),这一过程又叫“静态绑定/静态连接”

 

 

 

      初始化阶段:(动态绑定/动态连接)
              1. 执行时机——主动引用(new、反射、父类、执行主类包含main方法、调用静态方法
                  *new
                           ——执行父类的<clinit>()、执行本类的<clinit>()、执行父类的<client>、执行本类的<client>
 1 //例子(笔试常考题目) 2 public class Father { 3  4 private static int i =20; 5 static{ 6     System.out.println("Father;(类构造器-before)"+i); 7     i =50; 8     System.out.println("Father;(类构造器-after)"+i); 9 }10 public Father() {11     System.out.println("Father:(实例构造器)"+i);13 }14 15 }16 17 18 19 public class Son extends Father{20 21     private static int i =20;22     static{23         System.out.println("Son;(类构造器-before)"+i);24         i =50;25         System.out.println("Son;(类构造器-after)"+i);26     }27     public Son() {28         System.out.println("Son:(实例构造器)"+i);        30     }31 }32 33 34 35 public class Main {36 37     public static void main(String[] args) {38         new Son();39 40     }41 42 }43 44 //输出结果:45         /**46            *Father;(类构造器-before)2047            *Father;(类构造器-after)5048            *Son;(类构造器-before)2049            *Son;(类构造器-after)5050            *Father:(实例构造器)5051            *Son:(实例构造器)5052            */

 

                   2.主动引用、被动引用 
                            被动引用——会发生类加载,但不会初始化
                                         ——例子:略
  相关解决方案