JAVA类加载器概念与线程类加载器

类加载器的功能:通过一个类的全限定名来获取描述此类的二进制字节流的过程

java的类加载器大致可以分为两类,一类是系统提供的,一类是由应用开发人员编写的。系统提供的类加载器有以下三种:

引导类加载器(bootstrap class loader):用来加载 Java 的核心库(rt.jar),是用原生代码来实现的,并不继承自 java.lang.ClassLoader

扩展类加载器(extensions class loader):用来加载 Java 的扩展库。Java 虚拟机的实现会提供一个扩展库目录。该类加载器在此目录里面查找并加载 Java 类。

应用(系统)类加载器(Application class loader):根据 Java 应用的类路径(CLASSPATH)来加载 Java 类。一般来说,Java 应用的类都是由它来完成加载的。

另外还有开发人员自定义的Class loader。

系统在进行类加载时,会使用parent delegation原则。

parent delegation原则的工作过程:如果一个类加载器需要加载某个类时,它不会自己去尝试加载这个类,而是新该加载请求委托给其父类加载器去完成。同样父类加载器也会将该请求委托给其父类,直到顶层的Bootstrap Class Loader,引导类加载器根据请求,尝试加载类文件。如果加载类文件失败,则会反向由其子加载器去尝试加载,直到能加载成功的子加载器为止。简化后的顺序如下:

自定义类加载器->应用类加载器->扩展类加载器->引导类加载器

parent delegation的优势是什么?

通过parent delegation的方式,可以保证,java程序的稳定器,java判断是否是相同类,会要求两个类必须由同一类加载器加载。如果没有parent delegation的原则,对于同一个类,可能经由多个类加载器去加载。而每个类加载器加载到的类,被虚拟机认定为不同类,可想而知,这样会有多少操作和方法都不能执行。

parent delegation的实现代码?

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) {
               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.
             c = findClass(name);
         }
     }
     if (resolve) {
         resolveClass(c);
     }
     return c;
    }

如以上代码所示,parent delegation的实现非常简单,首先findLoadedClass(调用native方法)查找类是否已经被加载,如果类未被加载,则通过parent. loadClass(name, false);加载类,只有父加载器未加载成功,才会通过当前类加载器去加载。

特例-线程上下文类加载器

对于大多数的类加载都可以通过parent delegation的方式去现实,但有没有特例情况呢?如:jndi接口位于rt.jar包中,rt属于java核心类,其加载都通过bootstrap class loader完成。但jndi的实现,是由外部提供,不属于rt中。非rt包中的类,对于bootstrap class loader是不可见的,是无法加载的。那在加载jndi接口的同时,怎么样去加载jndi的实现呢?这时,可以通过线程上下文类加载器去实现,线程上下文类加载器并不是一个真正的类加载器,它是相对当前类加载器来说的。

把线程上下文类加载器定义成一种方式更合适,线程上下文类加载器其实就是一种可以在父类加载器的环境下,调用子类加载器的方式。表面上看起来似乎与parent delegation有冲突,其实不然,父类还是无法直接调用子类加载器的,能调用子类加载器的,只是在当前类加载器是父类加载器的线程环境,通过当前线程的getContextClassLoader()方法,去获取其它类加载器。示例如下:

 1 package com.csair.soc.thread;
 2
 3 import java.io.IOException;
 4 import java.io.InputStream;
 5
 6 public class MyClassLoader extends ClassLoader{
 7      @Override
 8      public Class<?> loadClass(String name) throws ClassNotFoundException {
 9             try {
10                 String fileName = name.substring(name.lastIndexOf("." ) + 1)
11                            + ".class";
12                 InputStream is = this.getClass().getResourceAsStream(fileName);
13                  if (is == null) {
14                       return super.loadClass(name);
15                 }
16                  byte[] b = new byte[is.available()];
17                 is.read(b);
18                  return defineClass(name, b, 0, b. length);
19            } catch (IOException e) {
20                  throw new ClassNotFoundException(name);
21            }
22      }
23 }

 1 package com.csair.soc.thread;
 2
 3 public class ThreadContextTest {
 4      public static void main(String[] args) throws ClassNotFoundException {
 5            ThreadTest tt = new ThreadTest();
 6             //设置线程的上下文类加载器
 7            tt.setContextClassLoader( new com.csair.soc.thread.MyClassLoader());
 8            tt.start();
 9      }
10      public static class ThreadTest extends Thread{
11          public void run() {
12                  try {
13                      System. out.println( "线程上下文类加载器:" + getContextClassLoader());
14                      System. out.println( "当前类加载器:" +this.getClass().getClassLoader());
15                      Class<?> class1 = getContextClassLoader().loadClass("com.csair.soc.thread.ThreadContextTest" );
16                      System. out.println( "加载class的类加载器:" +class1.getClassLoader());
17                 } catch (ClassNotFoundException e) {
18                      e.printStackTrace();
19                 }
20          }
21      }
22 }

输出结果:

线程上下文类加载器:[email protected]

当前类加载器:[email protected]

加载class的类加载器:[email protected]

通过线程上下文类加载器就能解决jndi的加载问题了,在当前类加载器为bootstrap class loader的线程环境下,线程通过getContextClassLoader()方法,获取提前设置的线程类加载器(Application class loader),去加载jndi的实现类。



classLoader怎么样持有实体类?为什么classLoader卸载后,实体类也跟着被卸载?

classloader会有自己的命名空间,命名空间由所有以此装载器为创始类装载器的类组成。对于class的卸载,Sun公司的原话是这么说的:"class or interfacemay be unloaded if and only if its class loader is unreachable. Classesloaded by the bootstrap loader may not be unloaded.当某个类的加载器被gc回收之后,该类才能被卸载

Class.forname()和ClassLoader.loadClass有什么区别,首次new XXX(),是怎么加载class的?

java提供两个方法来加载类

1、隐匿加载,如new XXX()方式来隐式加载class

2、显示加载,java.lang.Class里的forName()方法,java.lang.ClassLoader里的loadClass()

forName实际上调用native方法,forName0(className, true , ClassLoader.getCallerClassLoader ());

通过当前类加载器,来加载className。第二个参数为true表示装载类的时候是否初始化该类,即调用类的静态块的语句及初始化静态成员变量。

而使用loadClass加载类时,默认是不初始化该类的。

类加载器符合parent delegation的原则,那自定义的类加载器,为什么能加载class?

自定义类加载器,可以重写loadClass不遵守parent delegation原则。另外,有些类文件对于系统的类加载器不可见,或者说系统的类加载器找不到类文件,此时,自定义类加载器通过自定义的findClass方法去找到类文件。

时间: 2024-10-09 11:20:15

JAVA类加载器概念与线程类加载器的相关文章

Java虚拟机笔记 – JVM 自定义的类加载器的实现和使用2

1.用户自定义的类加载器: 要创建用户自己的类加载器,只需要扩展java.lang.ClassLoader类,然后覆盖它的findClass(String name)方法即可,该方法根据参数指定类的名字,返回对应的Class对象的引用. findClass protected Class<?> findClass(String name) throws ClassNotFoundException 使用指定的二进制名称查找类.此方法应该被类加载器的实现重写,该实现按照委托模型来加载类.在通过父

java类加载器学习2——自定义类加载器和父类委托机制带来的问题

一.自定义类加载器的一般步骤 Java的类加载器自从JDK1.2开始便引入了一条机制叫做父类委托机制.一个类需要被加载的时候,JVM先会调用他的父类加载器进行加载,父类调用父类的父类,一直到顶级类加载器.如果父类加载器加载不了,依次再使用其子类进行加载.当然这类所说的父类加载器,不一定他们之间是继承的关系,有可能仅仅是包装的关系. Java之所以出现这条机制,因为是处于安全性考虑.害怕用户自己定义class文件然后自己写一个类加载器来加载原本应该是JVM自己加载的类.这样会是JVM虚拟机混乱或者

Java虚拟机笔记 – JVM 自定义的类加载器的实现和使用

1.用户自定义的类加载器: 要创建用户自己的类加载器,只需要扩展java.lang.ClassLoader类,然后覆盖它的findClass(String name)方法即可,该方法根据参数指定类的名字,返回对应的Class对象的引用. findClass protected Class<?> findClass(String name) throws ClassNotFoundException 使用指定的二进制名称查找类.此方法应该被类加载器的实现重写,该实现按照委托模型来加载类.在通过父

java类加载机制概念

首先类加载在整个体系结构的哪一个环节呢?见红色圈住的部分. 类加载器分为那几个过程呢?五个过程 加载 根据类的全限定名(简单理解为类的绝对路径,见附录),找到指定的字节码文件,并在内存中生产一个java.lang.Class的对象,存放在方法区中. 验证 作用:确保字节码文件中包含的信息符合Class文件格式规范,对虚拟机来说是安全的. 规则一直在更新,大体有四种 文件格式验证 基于二进制字节流进行分析. 元数据验证(可以理解成是对类层面的信息验证) 对类的元数据进行语义分析. 字节码验证(对方

虚拟机类加载机制(2)——类加载器

<深入理解Java虚拟机>一书中将类的加载过程放到了类加载器前面一节,但在这里我想先讲“类加载器”.在上一篇类加载时机中我们用大量篇幅来讲解了类加载过程中的5个步骤的最后一步——初始化.在这一节中,我们实际是在讲解类加载过程5个步骤的第一步——加载. 我们再次回顾类加载过程的5个步骤: 类加载过程的“加载”步骤是通过类加载器(ClassLoader)来实现的.那么加载阶段做什么工作呢?“通过一个类的全限定名来获取描述此类的二进制字节流.”首先我们需要了解来自同一个Class文件的两个类是否一定

自定义类加载器的实现,类加载过程

为了安全 每个JAVA程序至少拥有三个类加载器 ·引导类加载器 bootstrap ·扩展类加载器 ext ·系统类加载器(应用类加载器)app 引导类加载器负责加载系统类(rt.jar),是虚拟机不可分割的一部分,C实现的,没有对应的classloader对象 例如String.class.getClassLoader()返回null 扩展类加载器用于从jre/lib/ext目录加载标准的扩展,将jar放入该目录,即使没有任何类路径,扩展类加载器也可以找到其中的各个类 系统类加载器用于加载应用

JVM类加载器原理与自定义类加载器

类加载器原理 JVM将class文件字节码文件加载到内存中, 并将这些静态数据转换成方法区中的运行时数据结构,在堆中生成一个代表这个类的java.lang.Class 对象,作为方法区类数据的访问入口. 类缓存 标准的Java SE类加载器可以按要求查找类,但一旦某个类被加载到类加载器中,它将维持加载(缓存)一段时间.不过,JVM垃圾收集器可以回收这些Class过象. 类加载器数状结构 引导类加载器(bootstrap class loader) 它用来加载Java的核心库(JAVA_HOME/

类加载机制的学习1______类加载器

在学习类加载机制之前,我们先了解一下类加载器,因为类加载器是类加载机制的前提.类加载器的主要任务就是:根据一个类的全限定名,将该类的字节码文件加载进JVM中,然后转换为一个对应类的Java.lang.Class对象实例.程序员也可以自定义类加载器,一般的将派生于抽象类ClassLoader的类加载器都划分为自定义类加载器. 在程序中我们最常见的类加载器时钟只有三个: Bootstrap ClassLoader ExtClassLoader: AppClassLoader:(加载classpath

Java常用类及反射,类加载

1.系统相关类 Java提供了System类和Runtime类来与程序运行的平台进行交互 A.System类代表当前Java程序的运行平台 a. System类是一个final类,该类的所有属性和方法都是静态的.可以不创建对象直接调用 b.属性:in .out .err c.常用方法: currentTimeMillis().exit().getProperties(). gc() B. Runtime类代表Java程序的运行时环境 注意: 应用程序不能创建自己的Runtime实例,但是可以通过