Java虚拟机:类加载器与双亲委派模型
2017-02-23 21:27
375 查看
学了挺久的java,也接触过java虚拟机,但是就是没有深入到源码中细细品读,也导致每次看到关于虚拟机的问题的时候都似曾相识却无从下手.这个系列就一点一点的品读java虚拟机.
最常见的类加载器始终只有3个:
* Bootstrap ClassLoader 启动类加载器(引导加载器)
* ExtClassLoader
* AppClassLoader
对应来看,每种类加载器对应的目录为:
测试代码:
输出结果:
以上测试的含义是:
1. 因为java.lang.System类包含在JAVA_HOME/lib目录中,测试由BootstrapClassLoader加载,由于启动类加载器本身由C++编写并嵌套在JVM内部,所以输出为null.
2. sun.text.resoutces.CollationData_ar类包含在JAVA_HOME/lib/ext扩展目录中,由ExtClassLoader加载.
3. 而我们的一般测试类在ClassPath目录中,因此由AppClassLoader加载.
查看ClassLoader类中loadClass的源码:
双亲委派模型的优点是使java类加载器具备优先级层级关系,越是基础的类越是被上层的类加载器加载,保证稳定运行.
**注意:**java虚拟机规范没有明确要求类加载器机制一定是双亲委派模型,只是建议.比如在Tomcat中,默认类加载器首先自己加载,失败时才给超类加载.
自定义类加载器只需继承抽象类ClassLoader并重写ClassLoader.
注意:怎样查看当前的ClassPath目录?
并在此打开命令行,用javac Test1.java 命令将其编译为Test1.class 文件.
输出为:
参考书目:《Java虚拟机精讲》 高翔龙
类加载器
从java虚拟机规范描述来看,JVM支持两种类加载器:引导类加载器(Bootstrap ClassLoader)和自定义类加载器(User-Defined ClassLoader).自定义类加载器派生于抽象类ClassLoader.最常见的类加载器始终只有3个:
* Bootstrap ClassLoader 启动类加载器(引导加载器)
* ExtClassLoader
* AppClassLoader
对应来看,每种类加载器对应的目录为:
类加载器 | 目录 | 备注 |
---|---|---|
Bootstrap ClassLoader | JAVA_HOME/lib,或者由选项-Xbootclasspath指定 | 用C++编写,嵌在JVM内部 |
ExtClassLoader | JAVA_HOME/ext | Java编写 派生于ClassLoader |
APPClassLoader | ClassPath |
public class Test { public static void main(String[] args) { //BootstrapClassLoader ClassLoader classLoader = System.class.getClassLoader(); System.out.println(null != classLoader ? classLoader.getClass().getName(): "name:null"); //ExtClassLoader System.out.println(CollationData_ar.class.getClassLoader().getClass().getName()); //AppClassLoader System.out.println(Test.class.getClassLoader().getClass().getName()); } }
输出结果:
name:null sun.misc.Launcher$ExtClassLoader sun.misc.Launcher$AppClassLoader
以上测试的含义是:
1. 因为java.lang.System类包含在JAVA_HOME/lib目录中,测试由BootstrapClassLoader加载,由于启动类加载器本身由C++编写并嵌套在JVM内部,所以输出为null.
2. sun.text.resoutces.CollationData_ar类包含在JAVA_HOME/lib/ext扩展目录中,由ExtClassLoader加载.
3. 而我们的一般测试类在ClassPath目录中,因此由AppClassLoader加载.
双亲委派模型
java程序中重要的一点是要保证一个类的全局唯一性.当程序中出现多个全限定名相同的类时,类加载器始终只加载其中的某一个类而不会都加载.查看ClassLoader类中loadClass的源码:
protected Class<?> loadClass(String name, boolean resolve) throws ClassNotFoundException { //1.检查目标类之前是否已经被加载过了 synchronized (getClassLoadingLock(name)) { // First, check if the class has already been loaded Class<?> c = findLoadedClass(name); 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; } }
双亲委派模型的优点是使java类加载器具备优先级层级关系,越是基础的类越是被上层的类加载器加载,保证稳定运行.
**注意:**java虚拟机规范没有明确要求类加载器机制一定是双亲委派模型,只是建议.比如在Tomcat中,默认类加载器首先自己加载,失败时才给超类加载.
自定义类加载器
自定义类加载器的需求并不多.但是,当有一些特殊需求,比如,当一个字节码文件在编译的时候进行了加密处理,那么类加载器在加载的时候首先就要解密,否则会认为它不是标准的字节码文件.自定义类加载器只需继承抽象类ClassLoader并重写ClassLoader.
注意:怎样查看当前的ClassPath目录?
System.getProperty("java.class.pat 4000 h");
准备工作
在F盘目录下创建Test1.java文件:public Test1{ public static void main(String[] args){ System.out.println("test1"); } }
并在此打开命令行,用javac Test1.java 命令将其编译为Test1.class 文件.
程序代码
public class MyClassLoader extends ClassLoader { private String byteCode_Path; public MyClassLoader(String byteCode_Path) { this.byteCode_Path = byteCode_Path; } @Override protected Class<?> findClass(String className) throws ClassNotFoundException { byte value[] = null; BufferedInputStream in = null; try { in = new BufferedInputStream(new FileInputStream(byteCode_Path + className + ".class")); value = new byte[in.available()];//将字节码全部读取到数组里 in.read(value); } catch (FileNotFoundException e) { e.printStackTrace(); } catch (IOException e) { e.printStackTrace(); } finally { if (null != in) { try { in.close(); } catch (IOException e) { e.printStackTrace(); } } } return defineClass(value, 0, value.length);//将byte数组转化为一个class对象实例 } public static void main(String[] args) throws ClassNotFoundException { MyClassLoader classLoader = new MyClassLoader("F:/"); System.out.println("加载目标类的类加载器->"+classLoader.loadClass("Test1").getClassLoader().getClass().getName()); System.out.println("当前类加载器的超类加载器:->"+classLoader.getParent().getClass().getName()); } }
输出为:
加载目标类的类加载器->MyClassLoader 当前类加载器的超类加载器:->sun.misc.Launcher$AppClassLoader
参考书目:《Java虚拟机精讲》 高翔龙
相关文章推荐
- 理解Java虚拟机(3)之.class文件加载双亲委派模型
- Java虚拟机之类加载(双亲委派模型)
- 双亲委派模型---类加载器(一)
- 黑马程序员--05.类加载器--03【从JVM加载类的过程再看类加载器】【从Java源码再看双亲委派模型】
- Java虚拟机类加载机制和双亲委派模型
- (转)类加载器与双亲委派模型
- JVM类加载机制详解(二)类加载器与双亲委派模型
- JVM类加载机制详解(二)类加载器与双亲委派模型
- 博客开篇——JAVA虚拟机,双亲委派模型
- 双亲委派模型与自定义类加载【转】
- JVM类加载的双亲委派模型
- JVM类加载时经典的双亲委派模型
- 双亲委派模型--类加载器
- 类加载双亲委派模型
- JVM类加载机制详解(二)类加载器与双亲委派模型
- JVM类加载机制详解(二)类加载器与双亲委派模型
- 【深入理解JVM】:类加载器与双亲委派模型
- 双亲委派模型和线程上下文类加载器
- Java高级篇(四十六)------【深入理解JVM】:类加载器与双亲委派模型
- 11.《深入理解Java虚拟机》类加载器与双亲委派模型