`

ClassLoader学习笔记

    博客分类:
  • java
 
阅读更多

针对这篇文章的学习摘录:原文:http://java.chinaitlab.com/base/804400.html

 

classloader最初为了满足java Applet而开发,将类从远程下载到浏览器中运行,后来在web容器和osgi中更为广泛应用。

 

Classloader的作用就是加载编译后的二进制 class JVM中。类加载器负责读取 Java 字节代码,并转换成 java.lang.Class 类的一个实例。每个这样的实例用来表示一个 Java 类。通过此实例的 newInstance()方法就可以创建出该类的一个对象。

 

而基本上所有的classloader都是java.lang.ClassLoader的一个实例(bootstrap classloader不是classLoader的子类)。ClassLoader根据一个字符串名称找到相应的字节码生成一个Class实例。

它有几个方法:

getParent() 返回该类加载器的父类加载器。

loadClass(String name) 加载名称为 name 的类,返回的结果是java.lang.Class 类的实例。

findClass(String name) 查找名称为 name 的类,返回的结果是 java.lang.Class 类的实例。

findLoadedClass(String name) 查找名称为 name 的已经被加载过的类,返回的结果是 java.lang.Class 类的实例。

defineClass(String name, byte[] b, int off, int len) 把字节数组 b 中的内容转换成 Java 类,返回的结果是 java.lang.Class 类的实例。这个方法被声明为 final 的。

resolveClass(Class<?> c) 链接指定的 Java 类,可能的意思是装入class相关的类。

Java 中的类加载器大致可以分成两类,一类是系统提供的,另外一类则是由 Java 应用开发人员编写的。系统提供的类加载器主要有下面三个

引导类加载器(bootstrap class loader:它用来加载 Java 的核心库,是用原生代码来实现的,并不继承自 java.lang.ClassLoader。(jre/lib下的几个包)。

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

  系统类加载器(system class loader:它根据 Java 应用的类路径(CLASSPATH)来加载 Java 类。一般来说,Java 应用的类都是由它来完成加载的。可以通过 ClassLoader.getSystemClassLoader() 来获取它。(java应用程序的加载类)

除了系统提供的类加载器以外,开发人员可以通过继承 java.lang.ClassLoader 类的方式实现自己的类加载器,以满足一些特殊的需求。

除了引导类加载器之外,所有的类加载器都有一个父类加载器。类加载器在尝试自己去查找某个类的字节代码并定义它时,会先代理给其父类加载器,由父类加载器先去尝试加载这个类,依次类推。首先理解JVM对于java类相同的判断。

jvm启动时,会启动jre/rt.jar里的类加载器:bootstrap classloader,用来加载java核心api;然后启动扩展类加载器ExtClassLoader加载扩展类,最后加载用户程序加载器AppClassLoader

通过以下代码可知bootstrap加载了那些核心库:

URL[] urls=sun.misc.Launcher.getBootstrapClassPath().getURLs();

      for (int i = 0; i < urls.length; i++) {

        System.out.println(urls[i].toExternalForm());

      }

我们不需要在系统属性CLASSPATH中指定这些核心库,因为JVM在启动的时候就自动加载它们了。

通过以下代码可知extensions class loader加载了什么类库

System.out.println(System.getProperty("java.ext.dirs"));

        ClassLoader extensionClassloader = ClassLoader.getSystemClassLoader().getParent();

        System.out.println("the parent of extension classloader : " + extensionClassloader.getParent());

结果:C:\Program Files\Java\jdk1.6.0_13\jre\lib\ext;C:\WINDOWS\Sun\Java\lib\ext

 the parent of extension classloader : null

结果说明:extension classloadersystem classloaderparent,而bootstrap classloaderextension classloaderparent,但它不是一个实际的classloader,所以为null

Java 虚拟机是如何判定两个 Java 类是相同的?Java 虚拟机不仅要看类的全名是否相同,还要看加载此类的类加载器是否一样。只有两者都相同的情况,才认为两个类是相同的。(每个 Java 类都维护着一个指向定义它的类加载器的引用,通过 getClassLoader() 方法就可以获取到此引用。)

了解了这一点之后,就可以理解代理模式的设计动机了。代理模式是为了保证 Java 核心库的类型安全。所有 Java 应用都至少需要引用 java.lang.Object 类,也就是说在运行的时候,java.lang.Object 这个类需要被加载到 Java 虚拟机中。如果这个加载过程由 Java 应用自己的类加载器来完成的话,很可能就存在多个版本的 java.lang.Object 类,而且这些类之间是不兼容的。通过代理模式,对于 Java 核心库的类的加载工作由引导类加载器来统一完成,保证了 Java 应用所使用的都是同一个版本的 Java 核心库的类,是互相兼容的。

假定一个类被两个classloader来加载,采用代理模式就可以保证核心库中的类是唯一的,都是经过同一个classloader加载的。

不同的类加载器为相同名称的类创建了额外的名称空间。相同名称的类可以并存在 Java 虚拟机中,只需要用不同的类加载器来加载它们即可。不同类加载器加载的类之间是不兼容的,这就相当于在 Java 虚拟机内部创建了一个个相互隔离的 Java 类空间。这种技术在许多框架中都被用到。

真正完成类的加载工作是通过调用 defineClass 来实现的;而启动类的加载过程是通过调用 loadClass 来实现的。在 Java 虚拟机判断两个类是否相同的时候,使用的是类的定义加载器。也就是说,哪个类加载器启动类的加载过程并不重要,重要的是最终定义这个类的加载器。一个类的定义加载器是它引用的其它类的初始加载器。

上下文类加载器:

java.lang.Thread 中的方法 getContextClassLoader() setContextClassLoader(ClassLoader cl) 用来获取和设置线程的上下文类加载器Java 应用运行的初始线程的上下文类加载器是系统类加载器。在线程中运行的代码可以通过此类加载器来加载类和资源。

前面提到的类加载器的代理模式并不能解决 Java 应用开发中会遇到的类加载器的全部问题。如java提供的服务提供者接口SPI,允许第三方为这些接口提供实现。常见的 SPI JDBCJCEJNDIJAXP JBI 等。这些 SPI 的接口由 Java 核心库来提供。这些 SPI 的实现代码很可能是作为 Java 应用所依赖的 jar 包被包含进来,可以通过类路径(CLASSPATH)来找到。

问题在于,SPI 的接口Java 核心库的一部分,是由引导类加载器来加载的;而SPI 实现类一般是由系统类加载器来加载的。引导类加载器是无法找到 SPI 的实现类的,因为它只加载 Java 的核心库。它也不能代理给系统类加载器,因为它是系统类加载器的祖先类加载器。也就是说,类加载器的代理模式无法解决这个问题。

线程上下文类加载器正好解决了这个问题。如果不做任何的设置,Java 应用的线程的上下文类加载器默认就是系统上下文类加载器。在 SPI 接口的代码中使用线程上下文类加载器,就可以成功的加载到 SPI 实现的类。线程上下文类加载器在很多 SPI 的实现中都会用到。

类加载器与 Web 容器

  对于运行在 Java EE 容器中的 Web 应用来说,类加载器的实现方式与一般的 Java 应用有所不同。不同的 Web 容器的实现方式也会有所不同。以 Apache Tomcat 来说,每个 Web 应用都有一个对应的类加载器实例。该类加载器也使用代理模式,所不同的是它是首先尝试去加载某个类,如果找不到再代理给父类加载器。这与一般类加载器的顺序是相反的。这是 Java Servlet 规范中的推荐做法,其目的是使得 Web 应用自己的类的优先级高于 Web 容器提供的类。这种代理模式的一个例外是:Java 核心库的类是不在查找范围之内的。这也是为了保证 Java 核心库的类型安全。

类加载器与 OSGi

  OSGi Java 上的动态模块系统。它为开发人员提供了面向服务和基于组件的运行环境,并提供标准的方式用来管理软件的生命周期。

OSGi 中的每个模块都有对应的一个类加载器。它负责加载模块自己包含的 Java 包和类。当它需要加载 Java 核心库的类时(以 java 开头的包和类),它会代理给父类加载器(通常是启动类加载器)来完成。当它需要加载所导入的 Java 类时,它会代理给导出此 Java 类的模块来完成加载。模块也可以显式的声明某些 Java 包和类,必须由父类加载器来加载。只需要设置系统属性 org.osgi.framework.bootdelegation 的值即可。

例子说明:假设有两个模块 bundleA bundleB,它们都有自己对应的类加载器 classLoaderA classLoaderB。在 bundleA 中包含类 com.bundleA.Sample,并且该类被声明为导出的,也就是说可以被其它模块所使用的。bundleB 声明了导入 bundleA 提供的类 com.bundleA.Sample,并包含一个类 com.bundleB.NewSample 继承自 com.bundleA.Sample。在 bundleB 启动的时候,其类加载器 classLoaderB 需要加载类 com.bundleB.NewSample,进而需要加载类 com.bundleA.Sample。由于 bundleB 声明了类 com.bundleA.Sample 是导入的,classLoaderB 把加载类 com.bundleA.Sample 的工作代理给导出该类的 bundleA 的类加载器 classLoaderAclassLoaderA 在其模块内部查找类 com.bundleA.Sample 并定义它,所得到的类 com.bundleA.Sample 实例就可以被所有声明导入了此类的模块使用。对于以 java 开头的类,都是由父类加载器来加载的。如果声明了系统属性 org.osgi.framework.bootdelegation=com.example.core.*,那么对于包 com.example.core 中的类,都是由父类加载器来完成的。

  OSGi 模块的这种类加载器结构,使得一个类的不同版本可以共存在 Java 虚拟机中,带来了很大的灵活性。不过它的这种不同,也会给开发人员带来一些麻烦,尤其当模块需要使用第三方提供的库的时候。下面提供几条比较好的建议:

  如果一个类库只有一个模块使用,把该类库的 jar 包放在模块中,在 Bundle-ClassPath 中指明即可。

  如果一个类库被多个模块共用,可以为这个类库单独的创建一个模块,把其它模块需要用到的 Java 包声明为导出的。其它模块声明导入这些类。

  如果类库提供了 SPI 接口,并且利用线程上下文类加载器来加载 SPI 实现的 Java 类,有可能会找不到 Java 类。如果出现了 NoClassDefFoundError 异常,首先检查当前线程的上下文类加载器是否正确。通过 Thread.currentThread().getContextClassLoader() 就可以得到该类加载器。该类加载器应该是该模块对应的类加载器。如果不是的话,可以首先通过 class.getClassLoader() 来得到模块对应的类加载器,再通过 Thread.currentThread().setContextClassLoader() 来设置当前线程的上下文类加载器。

两个相关文章:

http://www.blogjava.net/lhulcn618/archive/2006/05/25/48230.html

http://lyg5623.blog.163.com/blog/static/5327401120088284439854/

 

分享到:
评论

相关推荐

    java学习笔记整理

    【Java学习笔记整理】 Java是一种广泛使用的编程语言,它不仅是一种语言,还是一个软件开发平台和运行环境。Java分为三个主要版本:Java标准版(JSE)、Java缩微版(JME)和Java企业版(JEE)。JSE主要用于桌面应用...

    java之jvm学习笔记五(实践写自己的类装载器)

    这个“java之jvm学习笔记五(实践写自己的类装载器)”很可能是对这一主题的详细探讨。 类装载器在Java中的主要职责是动态加载类到JVM中。Java的类装载器分为三个基本层次:启动类装载器(Bootstrap ClassLoader)、...

    Java JDK 7学习笔记(国内第一本Java 7,前期版本累计销量5万册)

    丛书名: 学习笔记 出版社:清华大学出版社 ISBN:9787302282082 上架时间:2012-5-9 出版日期:2012 年5月 开本:16开 页码:564 版次:1-1 所属分类:计算机 &gt; 软件与程序设计 &gt; JAVA(J#) &gt; Java 编辑推荐   ...

    阿里P8 架构师整理Java学习笔记.pdf

    ### Java学习笔记知识点总结 #### 一、JVM与内存管理 **1.1 JVM基本概念** - **JVM(Java Virtual Machine)**: Java虚拟机是执行Java字节码的虚拟机,它提供了运行Java程序所需的环境。 **1.2 线程** - **线程...

    java 学习笔记(经典)

    根据提供的文件信息,这里将对Java学习笔记中的关键知识点进行详细阐述,主要涉及Java语言的基础概念、面向对象编程思想的应用以及Java类加载器的工作原理等内容。 ### Java学习基础 Java是一种广泛使用的高级编程...

    深入Java虚拟机JVM类加载学习笔记

    ### 深入Java虚拟机JVM类加载学习笔记 #### 一、Classloader机制解析 在Java虚拟机(JVM)中,类加载器(ClassLoader)是负责将类的`.class`文件加载到内存中的重要组件。理解类加载器的工作原理对于深入掌握JVM以及...

    J2EE-JDBC学习笔记

    ### J2EE-JDBC学习笔记知识点详述 #### JDBC驱动注册的三种方式 在Java开发中,使用JDBC(Java Database Connectivity)与数据库进行交互前,必须先注册对应的数据库驱动。J2EE环境下,通常有以下三种注册方式: ...

    springBoot学习笔记整理

    ### Spring Boot 学习笔记之热部署与代码变更检测 #### 概述 Spring Boot 是一个基于 Spring 的框架,旨在简化新 Spring 应用程序的初始设置和开发过程。Spring Boot 的目标是允许开发者“只是运行”,即尽可能...

    jVM学习笔记.ppt

    这一过程涉及到类加载机制,包括Bootstrap ClassLoader、Extension ClassLoader、App ClassLoader和Custom ClassLoader,它们按照特定的层次关系和加载顺序加载类,确保类的唯一性。 类执行机制中,JVM基于栈的架构...

    java学习笔记JDK6课件之十六

    Java学习笔记JDK6课件第16章深入讲解了反射机制,这是Java语言中一个强大的特性,允许我们在运行时动态地获取类的信息并进行操作。反射的核心是`java.lang.Class`类,它代表了Java应用程序运行时加载的类或接口实例...

    安卓逆向学习笔记之ART定制方案比较和流程.docx

    ### 安卓逆向学习笔记之ART定制方案比较和流程 #### 一、概述 本文档旨在探讨Android Runtime (ART)环境下,针对不同ART定制方案的对比分析,并介绍实现逆向工程过程中的具体步骤与技巧。逆向工程是研究软件内部...

    安卓逆向学习笔记之使用frida改进FART.docx

    ### 安卓逆向学习笔记之使用frida改进FART #### 一、FART工具原理及工作流程 FART(Find And Replace Tool)是一种针对Android应用进行逆向工程的工具,主要用于查找并替换Android应用中的指定代码或数据。本文档...

    Java学习笔记_内存管理.rar

    这份"Java学习笔记_内存管理.pdf"很可能是详细解析了Java如何进行内存分配、垃圾回收以及内存泄漏等相关概念。下面,我们将深入探讨Java内存管理的一些核心知识点。 1. **Java内存模型**: - **堆内存(Heap)**:...

    记录一些自己学习MySQL,JUC,JVM的学习笔记.zip

    3. 类加载器:学习不同的类加载器,如Bootstrap ClassLoader,Extension ClassLoader和App ClassLoader。 4. 垃圾回收:深入理解垃圾回收机制,如分代收集,可达性分析,以及不同GC算法,如Serial、Parallel、CMS和...

    JVM性能学习笔记思维导图

    本文将根据"JVM性能学习笔记思维导图"的主题,详细阐述JVM的主要组成部分,性能调优的关键点以及相关的工具与实践策略。** 1. **JVM结构与内存模型** - **类装载器(ClassLoader)**:负责加载类文件,确保类在运行...

    Thinkinjava学习笔记.pdf

    - JVM类加载器包括Bootstrap ClassLoader、Extension ClassLoader和Application ClassLoader,分别负责加载Java核心库、扩展库和应用程序类库。 8. Java中的异常处理 - Java使用try、catch、finally关键字来处理...

    安卓逆向学习笔记之FART主动调用组件设计和源码分析.docx

    ### 安卓逆向学习笔记之FART主动调用组件设计和源码分析 #### 一、概述 本文档旨在探讨安卓逆向工程中的一个重要工具——FART(Find And Replace Tool),并着重介绍如何利用该工具进行主动调用组件的设计与源码...

    jdk动态代理学习笔记

    1. **ClassLoader**: 目标对象的类加载器,用于生成代理类。 2. **interfaces**: 目标对象实现的接口数组,代理类将实现这些接口。 3. **InvocationHandler**: 实现了`InvocationHandler`的实例,负责处理方法调用。...

    JVM学习笔记

    ### JVM学习笔记知识点详解 #### 一、JVM的基本结构 **JVM(Java Virtual Machine,Java虚拟机)**是一种可以执行Java字节码的虚拟机。它为Java提供了平台无关性,使得Java代码可以在任何安装了JVM的平台上运行。 ...

    Java虚拟机JVM类加载学习笔记

    JVM的类加载器(ClassLoader)负责将编译后的`.class`文件加载到内存中,为程序执行做好准备。当类被加载时,JVM会在内存的运行时数据区的方法区内存储类的信息,并在堆中创建一个`java.lang.Class`对象来封装这些...

Global site tag (gtag.js) - Google Analytics