wenwan il y a 8 ans
Parent
commit
fc94fab90d
3 fichiers modifiés avec 134 ajouts et 2 suppressions
  1. BIN
      basic-knowledge/img/12.png
  2. 3 2
      basic-knowledge/java.md
  3. 131 0
      basic-knowledge/类加载器.md

BIN
basic-knowledge/img/12.png


+ 3 - 2
basic-knowledge/java.md

@@ -9,12 +9,13 @@
 * 	[ThreadLocal原理机制](ThreadLocal原理机制.md)
 * 	HashMap的扩容机制
 * 	NIO
-* 	ClassLoader
 * 	[java修饰词](java修饰词.md)
 * 	[各种坑](各种坑.md)
 
 
 ### 进阶
+
+* 	[类加载器](类加载器.md)
 *  	[jvm 内存结构](jvm内存结构.md)
 *  	jvm 性能调优
-* 	常用的jdk命令
+* 	常用的jdk命令

+ 131 - 0
basic-knowledge/类加载器.md

@@ -0,0 +1,131 @@
+## 类加载器
+
+---
+
+
+java允许通过编码方式间接对Class进行操作,当一个class文件被类加载器装载后,在JVM就会形成一份描述Class结构的元数据,通过这些元数据可以获知Class的结构信息:比如构造器、属性和方法。同时也允许用户根据这些元信息对象间接调用Class对象的功能,即“反射”机制。
+
+**类装载器(ClassLoader)是寻找字节码文件并加载到jvm中。步骤分为三步:**
+
+1)装载:查找和导入Class文件
+
+2)链接:执行校验、准备和解析
+
+```
+  校验:检查载入的Class文件数据的正确性
+  准备:给变量分配存储空间
+  解析:将符号引用转成直接引用
+```
+      
+3)初始化:给类的静态变量或静态代码块执行初始化工作
+
+
+类的装载器有三个:根装载器(用C++编写),负责装载JRE的核心类库。ExtClassLoader扩展类加载器,负责加载ext目录下的jar包。AppClassLoader系统类装载器,负责装载Classpath路径下的类包。这三个类装载器存在父子层级关系,根装载器是ExtClassLoader的父装载器,ExtClassLoader是AppClassLoader的父装载器。一般来说,Java 应用的类都是由AppClassLoader来完成加载的。可以通过 ClassLoader.getSystemClassLoader() 来获取它。一般来说,开发人员编写的类加载器的父类加载器是 应用类加载器  Application ClassLoader  
+
+除了系统提供的类加载器以外,开发人员可以通过继承 java.lang.ClassLoader 类的方式实现自己的类加载器,以满足一些特殊的需求。
+
+除了根类加载器之外,所有的类加载器都有一个父类加载器。可以通过 getParent()方法得到。**不同的类加载器为相同名字的类创建了额外的名称空间,所以相同名称的类才可以并存在 Java 虚拟机中,只需要用不同的类加载器来加载它们即可。**不同类加载器加载的类之间是不兼容的(相当于两个不同的类型),这就相当于在 Java 虚拟机内部创建了一个个相互隔离的 Java 类空间。这种技术在许多框架中都被用到,如OSGI等。
+
+jvm启动时,会启动jre/rt.jar里的类加载器:bootstrap classloader,用来加载java核心api;然后启动扩展类加载器ExtClassLoader加载扩展类,并加载用户程序加载器AppClassLoader,并指定ExtClassLoader为他的父类;
+
+
+![image](img/12.png)
+
+CommonClassLoader、CatalinaClassLoader和SharedClassLoader与具体部署的Web应用无关,而WebappClassLoader则对应Web应用,每个Web应用都会有独立的类加载器,从而实现类的隔离。当类被加载时,会先检查在内存中是否已经被加载,如果是,则不再加载,如果没有,再由AppClassLoader来加载,先从jar包里找,没有再从classpath里找;如果自定义loader类,就会存在命名空间的情况,不同的加载器加载同一个类时,产生的实例其实是不同的。
+
+**实例 :**
+
+我们可以考虑这样一种情况,假设我们自定义了一个ClientDefClassLoader,我们使用这个自定义的ClassLoader加载java.lang.String,那么这里String是否会被这个ClassLoader加载呢?事实上java.lang.String这个类并不是被这个ClientDefClassLoader加载,而是由bootstrap classloader进行加载,为什么会这样?实际上这就是双亲委托模式的原因,因为在任何一个自定义ClassLoader加载一个类之前,它都会先委托它的父ClassLoader进行加载(递归模式,向上寻找),只有当父亲ClassLoader无法加载成功后,才会由自己加载,在上面这个例子里,因为java.lang.String是属于java核心API的一个类,所以当使用ClientDefClassLoader加载它的时候,该ClassLoader会先委托它的父ClassLoader进行加载,上面讲过,当ClassLoader的parent为null时,ClassLoader就是bootstrap classloader,所以在ClassLoader的最顶层就是bootstrap classloader,因此最终委托到bootstrap classloader的时候,bootstrap classloader就会返回String的Class。 
+
+**双亲委托模式的优势:**
+
+a)避免重复加载,当父类装载器已经加载,则子类装载器没有必要再加载一次
+
+b)安全因素。使用自定义的String来动态替代java核心api中定义类型,这样会存在非常大的安全隐患,而双亲委托的方式,就可以避免这种情况,因为String已经在启动时被加载,所以用户自定义类是无法加载一个自定义的ClassLoader
+
+
+ClassLoader类
+
+```
+protected synchronized Class<?> loadClass(String name, boolean resolve)
+ throws ClassNotFoundException
+    {
+ // First, check if the class has already been loaded
+ Class c = findLoadedClass(name);
+ if (c == null) {
+     try {
+  if (parent != null) {
+      // 父类装载器加载,false表示加载的类不会初始化
+      c = parent.loadClass(name, false);
+  } else {
+     // 根类装载器加载
+      c = findBootstrapClass0(name);
+  }
+     } catch (ClassNotFoundException e) {
+         // If still not found, then invoke findClass in order
+         // to find the class.
+         c = findClass(name);
+     }
+ }
+ if (resolve) {
+     resolveClass(c);
+ }
+ return c;
+    }
+```
+
+```
+private Class findBootstrapClass0(String name)
+ throws ClassNotFoundException
+    {
+ check();
+ if (!checkName(name))
+     throw new ClassNotFoundException(name);
+ return findBootstrapClass(name);
+    }
+    
+```
+check()方法来判断类装载器是否已经初始化;checkName(name)判断name是否合法(不为空&&合法的二进制名字),最后调用findBootstrapClass(name)
+
+```
+private native Class findBootstrapClass(String name)
+ throws ClassNotFoundException;
+```
+
+findBootstrapClass方法是一个native方法,这是我们的root loader,这个载入方法并非是由JAVA所写,而是C++写的,它最终调用JVM中的原生findBootstrapClass方法来完成类的加载。 
+
+**完整的加载class步骤:**
+
+1.检测此Class是否载入过(即在cache中是否有此Class),如果有到8,如果没有到2
+
+2.如果parent classloader不存在(没有parent,那parent一定是bootstrap classloader了),到4
+
+3.请求parent classloader载入,如果成功到8,不成功到5
+
+4.请求jvm从bootstrap classloader中载入,如果成功到8
+
+5.寻找Class文件(从与此classloader相关的类路径中寻找)。如果找不到则到7
+
+6.从文件中载入Class,到8
+
+7.抛出ClassNotFoundException
+
+8.返回Class
+
+
+**类的完整标识是(classLoader,package,className)**
+
+
+**Class类中有个静态方法forName,这个方法和ClassLoader中的loadClass方法的目的一样,都是用来加载class的,但是两者在作用上却有所区别???**
+
+Class<?> loadClass(String name) 
+
+Class<?> loadClass(String name, boolean resolve) 
+
+我们看到上面两个方法声明,第二个方法的第二个参数用于设置加载类的时候是否链接该类(上文中装载类文件的第二步),true链接,否则就不链接。Class类的forName方法则相反,使用forName加载时会将Class进行解析和初始化。
+
+例如:JDBC DRIVER的加载,我们在加载JDBC驱动的时候都是使用的forName而非是ClassLoader的loadClass方法呢?我们知道,JDBC驱动是通过DriverManager,必须在DriverManager中注册,如果驱动类没有被初始化,则不能注册到DriverManager中,因此必须使用forName而不是用LoadClass。 
+
+
+
+