浅谈JVM-类加载器结构与双亲委派机制
一、类加载器结构
1、引导类加载器(bootstrap class loader)
它用来加载Java的核心库(JAVA_HOME/jre/lib/rt.jar),是用原声代码来实现的,并不继承自java.lang.Classloader。
加载扩展类和应用程序类加载器。并制定指定的父类加载器。
2、扩展类加载器(extensions class loader)
用来加载Java的扩展库(JAVA_HOME/JRE/EXT/*.jar),Java虚拟机的实现会提供一个扩展库目录。该加载器在此目录里面查找并加载Java类。
3、应用程序类(application class loader)
它根据Java应用程序的类路径(classpath,java.class.path)路径类。一般来说,系统(应用程序)类都是由它来加载。
4、自定义类
继承java.lang.ClassLoader类的方式实现自己的类加载器,以满足一些特殊的需求。
二、类加载作用与API
三、双亲委派机制
双亲委派机制实际上就是使用代理模式(交给其它类加载器完成)。
某个特定的类加载器在接到加载器请求时,首先将加载任务委托给父加载器,一直高层次加载器委托。若加载器可完成其加载任务,就成功返回;只有父加载器无法完成此加载任务,才自己加载。
作用:保证java核心库的类型安全。
思考:如何保证安全?假设我们自定义了java.lang.String类,这属于系统类是不允许被加载的。首先Application Class Loader向上抛,直到抛到Bootstrap Class Loader类,然后Bootstrap Class Loader一看,咦,我核心库里面有java.lang.String类,就直接把rt.jar的代码加载进去。我们自定义的java.lang.String就孤单的被抛弃了。
四、自定义类加载器
1 public class FileSystemClassLoader extends ClassLoader { 2 3 //HelloWorld --> f:/myjava/ HelloWorld.class 4 private String rootDir; 5 6 public FileSystemClassLoader(String rootDir){ 7 this.rootDir = rootDir; 8 } 9 10 @Override 11 protected Class> findClass(String name) throws ClassNotFoundException { 12 13 Class> c = findLoadedClass(name); 14 15 //应该要先查询有没有加载过这个类。如果已经加载,则直接返回加载好的类。如果没有,则加载新的类。 16 if(c!=null){ 17 return c; 18 }else{ 19 ClassLoader parent = this.getParent(); 20 try { 21 c = parent.loadClass(name); //委派给父类加载 22 } catch (Exception e) { 23 // e.printStackTrace(); 24 } 25 26 if(c!=null){ 27 return c; 28 }else{ 29 byte[] classData = getClassData(name); 30 if(classData==null){ 31 throw new ClassNotFoundException(); 32 }else{ 33 c = defineClass(name, classData, 0,classData.length); 34 } 35 } 36 37 } 38 39 return c; 40 41 } 42 43 private byte[] getClassData(String classname){ //com.bjsxt.test.User d:/myjava/ com/bjsxt/test/User.class 44 String path = rootDir +"/"+ classname.replace('.', '/')+".class"; 45 46 // IOUtils,可以使用它将流中的数据转成字节数组 47 InputStream is = null; 48 ByteArrayOutputStream baos = new ByteArrayOutputStream(); 49 try{ 50 is = new FileInputStream(path); 51 52 byte[] buffer = new byte[1024]; 53 int temp=0; 54 while((temp=is.read(buffer))!=-1){ 55 baos.write(buffer, 0, temp); 56 } 57 58 return baos.toByteArray(); 59 }catch(Exception e){ 60 e.printStackTrace(); 61 return null; 62 }finally{ 63 try { 64 if(is!=null){ 65 is.close(); 66 } 67 } catch (IOException e) { 68 e.printStackTrace(); 69 } 70 try { 71 if(baos!=null){ 72 baos.close(); 73 } 74 } catch (IOException e) { 75 e.printStackTrace(); 76 } 77 } 78 79 } 80 }
测试类:当同一个类被不同加载器加载的时候,JVM认为它们不是同一个类。
1 package com.aaron.classloader; 2 3 public class Test { 4 public static void main(String[] args) throws Exception{ 5 FileSystemClassLoader loader = new FileSystemClassLoader("f:/myjava"); 6 FileSystemClassLoader loader2 = new FileSystemClassLoader("f:/myjava"); 7 8 Class> c = loader.loadClass("HelloWorld"); 9 Class> c2 = loader.loadClass("HelloWorld"); 10 Class> c3 = loader2.loadClass("HelloWorld"); 11 12 Class> c4 = loader2.loadClass("java.lang.String"); 13 Class> c5 = loader2.loadClass("com.aaron.classloader.HelloWorld"); 14 15 16 System.out.println(c.hashCode()); 17 System.out.println(c2.hashCode()); 18 System.out.println(c3.hashCode()); //同一个类,被不同的加载器加载,JVM认为也是不相同的类 19 System.out.println(c4.hashCode()); 20 System.out.println(c4.getClassLoader());//引导类加载器 21 System.out.println(c3.getClassLoader()); //自定义的类加载器 22 System.out.println(c5.getClassLoader()); //系统默认的类加载器 23 24 } 25 }
测试结果
深入学习参考:http://blog.csdn.net/zhoudaxia/article/details/35824249