(转) Java虚拟机类加载顺序研究

本贴最后更新于 3246 天前,其中的信息可能已经时移世异

当JVM(Java虚拟机)启动时,会形成由三个类加载器组成的初始类加载器层次结构:

        Bootstrap Classloader
                        |
        Extension Classloader
                        |
        System Classloader

1.Bootstrap Classloader -引导(也称为原始)类加载器,这个加载器的是非常特殊的,它实际上不是 java.lang.ClassLoader的子类,而是由JVM自身实现的

它所加载的有如下的资源:

D:/Program Files/Java/jre6/lib/resources.jar
D:/Program Files/Java/jre6/lib/rt.jar
D:/Program Files/Java/jre6/lib/sunrsasign.jar
D:/Program Files/Java/jre6/lib/jsse.jar
D:/Program Files/Java/jre6/lib/jce.jar
D:/Program Files/Java/jre6/lib/charsets.jar
D:/Program Files/Java/jre6/classes

这时大家知道了为什么我们不需要在系统属性CLASSPATH中指定这些类库了吧,因为JVM在启动的时候就自动加载它们了。

2.Extension Classloader -扩展类加载器 ExtClassLoader,它负责加载JRE的扩展目录(JAVA_HOME/jre/lib/ext或者由java.ext.dirs系统属性指定的)中JAR的类包,它已经属于最上层的加载器了。它所加载的有如下的资源:

D:/Program Files/Java/jre6/lib/ext;C:/WINDOWS/Sun/Java/lib/ext


3. System Classloader -系统(也称为应用)类加载器AppClassLoader 加载来自在命令java中的-classpath或者java.class.path系统属性或者 CLASSPATH操作系统属性所指定的JAR类包和类路径。也就是加载项目中用到的第三方包和项目中的类,它的父加载器为ExtClassLoader

它所加载的有如下的资源:

E:/works/encoder/lib/antlr-2.7.6.jar
E:/works/encoder/lib/cglib-2.2.jar
E:/works/encoder/lib/cglib-nodep-2.1_3.jar
E:/works/encoder/lib/commons-codec-1.1.jar
E:/works/encoder/lib/commons-collections-3.2.jar
E:/works/encoder/lib/commons-lang-2.1.jar
E:/works/encoder/lib/commons-logging-1.0.4.jar
E:/works/encoder/lib/commons-logging.jar
E:/works/encoder/lib/dom4j-1.4.jar
E:/works/encoder/lib/ehcache-0.9.jar
E:/works/encoder/lib/gameserver_core_1.0.1.jar
E:/works/encoder/lib/hibernate-annotations.jar
E:/works/encoder/lib/hibernate-commons-annotations.jar
E:/works/encoder/lib/hibernate-entitymanager.jar
E:/works/encoder/lib/hibernate-tools.jar
E:/works/encoder/lib/hibernate2.jar
E:/works/encoder/lib/hibernate3.jar
E:/works/encoder/lib/jakarta-oro.jar
E:/works/encoder/lib/jasypt-1.5.jar
E:/works/encoder/lib/javassist-3.9.0.GA.jar
E:/works/encoder/lib/jta.jar
E:/works/encoder/lib/jzlib-1.0.7.jar
E:/works/encoder/lib/libthrift.jar
E:/works/encoder/lib/log4j-1.2.15.jar
E:/works/encoder/lib/mina-integration-beans-2.0.0-M4.jar
E:/works/encoder/lib/MySQL-connector-java-5.0.8-bin.jar
E:/works/encoder/lib/persistence.jar
E:/works/encoder/lib/proxool-0.9.1.jar
E:/works/encoder/lib/proxool-cglib.jar
E:/works/encoder/lib/slf4j-api-1.5.6.jar
E:/works/encoder/lib/slf4j-log4j12-1.5.6.jar
E:/works/encoder/lib/spring.jar
E:/works/encoder/lib/springside3-core-3.1.4.jar
E:/works/encoder/lib/xerces-2.6.2.jar
E:/works/encoder/lib/xml-apis.jar

classloader 加载类用的是全盘负责委托机制。所谓全盘负责,即是当一个classloader加载一个Class的时候,这个Class所依赖的和引用的所有 Class也由这个classloader负责载入,除非是显式的使用另外一个classloader载入;委托机制则是先让parent(父)类加载器 (而不是super,它与parent classloader类不是继承关系)寻找,只有在parent找不到的时候才从自己的类路径中去寻找。此外类加载还采用了cache机制,也就是如果 cache中保存了这个Class就直接返回它,如果没有才从文件中读取和转换成Class,并存入cache,这就是为什么我们修改了Class但是必须重新启动JVM才能生效的原因。
类加载器的顺序是:
先是bootstrap classloader,然后是extension classloader,最后才是system classloader。大家会发现加载的Class越是重要的越在靠前面。这样做的原因是出于安全性的考虑,试想如果system classloader“亲自”加载了一个具有破坏性的“java.lang.System”类的后果吧。这种委托机制保证了用户即使具有一个这样的类,也把它加入到了类路径中,但是它永远不会被载入,因为这个类总是由bootstrap classloader来加载的。

System.out.println("bootstrap classloader -引导(也称为原始)类加载器");
  URL[] urls=sun.misc.Launcher.getBootstrapClassPath().getURLs();
     for (int i = 0; i < urls.length; i++) {
       System.out.println(urls[i].toExternalForm());
     }
  System.out.println("extension classloader -扩展类加载器");
  System.out.println(System.getProperty("java.ext.dirs"));
  System.out.println("system classloader -系统(也称为应用)类加载器");
  System.out.println(System.getProperty("java.class.path").replace(";", "/n"));

========================================

JAVA中的每一个类都是通过类加载器加载到内存中的。对于类加载器的工作流程如下表示:
1.searchfile() 
找到我所要加载的类文件。(抛除JAR包的概念,现在只是要加载一个.class文件)
2.loadDataClass()
读取这个类文件的字节码。
3.defineClass()
加载类文件。(加载的过程其实很复杂,我们现在先不研究它。)
从这个过程中我们能很清楚的发现,自定义的类加载能够很轻松的控制每个类文件的加载过程。这样在第二步(loadDataClass)和第三步(difineClass)之间,我们将会有自己的空间灵活的控制这个过程。
我们加密解密的技术就应用到这里。

了解ClassLoader
1, 什么是 ClassLoader? 
Java 程序并不是一个可执行文件,是需要的时候,才把装载到 JVM中。ClassLoader 做的工作就是 JVM 中将类装入内存。 而且,Java ClassLoader 就是用 Java 语言编写的。这意味着您可以创建自己的 ClassLoader 
ClassLoader 的基本目标是对类的请求提供服务。当 JVM 需要使用类时,它根据名称向 ClassLoader 请求这个类,然后 ClassLoader 试图返回一个表示这个类的 Class 对象。 通过覆盖对应于这个过程不同阶段的方法,可以创建定制的 ClassLoader。
2, 一些重要的方法
A) 方法 loadClass 
ClassLoader.loadClass() 是 ClassLoader 的入口点。该方法的定义如下: 
Class loadClass( String name, boolean resolve ;
name JVM 需要的类的名称,如 Foo 或 java.lang.Object。
resolve 参数告诉方法是否需要解析类。在准备执行类之前,应考虑类解析。并不总是需要解析。如果 JVM 只需要知道该类是否存在或找出该类的超类,那么就不需要解析。

B) 方法 defineClass 
defineClass 方法是 ClassLoader 的主要诀窍。该方法接受由原始字节组成的数组并把它转换成 Class 对象。原始数组包含如从文件系统或网络装入的数据。defineClass 管理 JVM 的许多复杂、神秘和倚赖于实现的方面 -- 它把字节码分析成运行时数据结构、校验有效性等等。不必担心,您无需亲自编写它。事实上,即使您想要这么做也不能覆盖它,因为它已被标记成final的。

C) 方法 findSystemClass 
findSystemClass 方法从本地文件系统装入文件。它在本地文件系统中寻找类文件,如果存在,就使用 defineClass 将原始字节转换成 Class 对象,以将该文件转换成类。当运行 Java 应用程序时,这是 JVM 正常装入类的缺省机制。(Java 2 中 ClassLoader 的变动提供了关于 Java 版本 1.2 这个过程变动的详细信息。) 对于定制的 ClassLoader,只有在尝试其它方法装入类之后,再使用 findSystemClass。原因很简单:ClassLoader 是负责执行装入类的特殊步骤,不是负责所有类。例如,即使 ClassLoader 从远程的 Web 站点装入了某些类,仍然需要在本地机器上装入大量的基本 Java 库。而这些类不是我们所关心的,所以要 JVM 以缺省方式装入它们:从本地文件系统。这就是 findSystemClass 的用途。

D) 方法 resolveClass 
正如前面所提到的,可以不完全地(不带解析)装入类,也可以完全地(带解析)装入类。当编写我们自己的 loadClass 时,可以调用 resolveClass,这取决于 loadClass 的 resolve 参数的值。


E) 方法 findLoadedClass 
findLoadedClass 充当一个缓存:当请求 loadClass 装入类时,它调用该方法来查看 ClassLoader 是否已装入这个类,这样可以避免重新装入已存在类所造成的麻烦。应首先调用该方法。

3, 怎么组装这些方法 
1) 调用 findLoadedClass 来查看是否存在已装入的类。
2) 如果没有,那么采用那种特殊的神奇方式来获取原始字节。
3) 如果已有原始字节,调用 defineClass 将它们转换成 Class 对象。
4) 如果没有原始字节,然后调用 findSystemClass 查看是否从本地文件系统获取类。
5) 如果 resolve 参数是 true,那么调用 resolveClass 解析 Class 对象。
6) 如果还没有类,返回 ClassNotFoundException。

4,Java 2 中 ClassLoader 的变动
1)loadClass 的缺省实现 
定制编写的 loadClass 方法一般尝试几种方式来装入所请求的类,如果您编写许多类,会发现一次次地在相同的、很复杂的方法上编写变量。 在 Java 1.2 中 loadClass 的实现嵌入了大多数查找类的一般方法,并使您通过覆盖 findClass 方法来定制它,在适当的时候 findClass 会调用 loadClass。 这种方式的好处是您可能不一定要覆盖 loadClass;只要覆盖 findClass 就行了,这减少了工作量。

2)新方法:findClass
loadClass 的缺省实现调用这个新方法。findClass 的用途包含您的 ClassLoader 的所有特殊代码,而无需要复制其它代码(例如,当专门的方法失败时,调用系统 ClassLoader)。

3) 新方法:getSystemClassLoader 
如果覆盖 findClass 或 loadClass,getSystemClassLoader 使您能以实际 ClassLoader 对象来访问系统 ClassLoader(而不是固定的从 findSystemClass 调用它)。

4) 新方法:getParent 
为了将类请求委托给父代 ClassLoader,这个新方法允许 ClassLoader 获取它的父代 ClassLoader。当使用特殊方法,定制的 ClassLoader 不能找到类时,可以使用这种方法。
父代 ClassLoader 被定义成创建该 ClassLoader 所包含代码的对象的 ClassLoader。

  • Java

    Java 是一种可以撰写跨平台应用软件的面向对象的程序设计语言,是由 Sun Microsystems 公司于 1995 年 5 月推出的。Java 技术具有卓越的通用性、高效性、平台移植性和安全性。

    3200 引用 • 8216 回帖

相关帖子

欢迎来到这里!

我们正在构建一个小众社区,大家在这里相互信任,以平等 • 自由 • 奔放的价值观进行分享交流。最终,希望大家能够找到与自己志同道合的伙伴,共同成长。

注册 关于
请输入回帖内容 ...

推荐标签 标签

  • Docker

    Docker 是一个开源的应用容器引擎,让开发者可以打包他们的应用以及依赖包到一个可移植的容器中,然后发布到任何流行的操作系统上。容器完全使用沙箱机制,几乎没有性能开销,可以很容易地在机器和数据中心中运行。

    495 引用 • 931 回帖
  • 负能量

    上帝为你关上了一扇门,然后就去睡觉了....努力不一定能成功,但不努力一定很轻松 (° ー °〃)

    89 引用 • 1251 回帖 • 408 关注
  • Oracle

    Oracle(甲骨文)公司,全称甲骨文股份有限公司(甲骨文软件系统有限公司),是全球最大的企业级软件公司,总部位于美国加利福尼亚州的红木滩。1989 年正式进入中国市场。2013 年,甲骨文已超越 IBM,成为继 Microsoft 后全球第二大软件公司。

    107 引用 • 127 回帖 • 339 关注
  • 思源笔记

    思源笔记是一款隐私优先的个人知识管理系统,支持完全离线使用,同时也支持端到端加密同步。

    融合块、大纲和双向链接,重构你的思维。

    25409 引用 • 105066 回帖 • 1 关注
  • Anytype
    3 引用 • 31 回帖 • 15 关注
  • Electron

    Electron 基于 Chromium 和 Node.js,让你可以使用 HTML、CSS 和 JavaScript 构建应用。它是一个由 GitHub 及众多贡献者组成的活跃社区共同维护的开源项目,兼容 Mac、Windows 和 Linux,它构建的应用可在这三个操作系统上面运行。

    15 引用 • 136 回帖 • 5 关注
  • 微服务

    微服务架构是一种架构模式,它提倡将单一应用划分成一组小的服务。服务之间互相协调,互相配合,为用户提供最终价值。每个服务运行在独立的进程中。服务于服务之间才用轻量级的通信机制互相沟通。每个服务都围绕着具体业务构建,能够被独立的部署。

    96 引用 • 155 回帖 • 2 关注
  • 锤子科技

    锤子科技(Smartisan)成立于 2012 年 5 月,是一家制造移动互联网终端设备的公司,公司的使命是用完美主义的工匠精神,打造用户体验一流的数码消费类产品(智能手机为主),改善人们的生活质量。

    4 引用 • 31 回帖 • 3 关注
  • 电影

    这是一个不能说的秘密。

    122 引用 • 608 回帖
  • 房星科技

    房星网,我们不和没有钱的程序员谈理想,我们要让程序员又有理想又有钱。我们有雄厚的房地产行业线下资源,遍布昆明全城的 100 家门店、四千地产经纪人是我们坚实的后盾。

    6 引用 • 141 回帖 • 590 关注
  • 外包

    有空闲时间是接外包好呢还是学习好呢?

    26 引用 • 233 回帖 • 2 关注
  • Dubbo

    Dubbo 是一个分布式服务框架,致力于提供高性能和透明化的 RPC 远程服务调用方案,是 [阿里巴巴] SOA 服务化治理方案的核心框架,每天为 2,000+ 个服务提供 3,000,000,000+ 次访问量支持,并被广泛应用于阿里巴巴集团的各成员站点。

    60 引用 • 82 回帖 • 615 关注
  • PHP

    PHP(Hypertext Preprocessor)是一种开源脚本语言。语法吸收了 C 语言、 Java 和 Perl 的特点,主要适用于 Web 开发领域,据说是世界上最好的编程语言。

    180 引用 • 408 回帖 • 484 关注
  • Quicker

    Quicker 您的指尖工具箱!操作更少,收获更多!

    37 引用 • 157 回帖 • 3 关注
  • Access
    1 引用 • 3 回帖 • 1 关注
  • OkHttp

    OkHttp 是一款 HTTP & HTTP/2 客户端库,专为 Android 和 Java 应用打造。

    16 引用 • 6 回帖 • 85 关注
  • 爬虫

    网络爬虫(Spider、Crawler),是一种按照一定的规则,自动地抓取万维网信息的程序。

    106 引用 • 275 回帖
  • CodeMirror
    2 引用 • 17 回帖 • 162 关注
  • 百度

    百度(Nasdaq:BIDU)是全球最大的中文搜索引擎、最大的中文网站。2000 年 1 月由李彦宏创立于北京中关村,致力于向人们提供“简单,可依赖”的信息获取方式。“百度”二字源于中国宋朝词人辛弃疾的《青玉案·元夕》词句“众里寻他千百度”,象征着百度对中文信息检索技术的执著追求。

    63 引用 • 785 回帖 • 94 关注
  • 大疆创新

    深圳市大疆创新科技有限公司(DJI-Innovations,简称 DJI),成立于 2006 年,是全球领先的无人飞行器控制系统及无人机解决方案的研发和生产商,客户遍布全球 100 多个国家。通过持续的创新,大疆致力于为无人机工业、行业用户以及专业航拍应用提供性能最强、体验最佳的革命性智能飞控产品和解决方案。

    2 引用 • 14 回帖
  • CentOS

    CentOS(Community Enterprise Operating System)是 Linux 发行版之一,它是来自于 Red Hat Enterprise Linux 依照开放源代码规定释出的源代码所编译而成。由于出自同样的源代码,因此有些要求高度稳定的服务器以 CentOS 替代商业版的 Red Hat Enterprise Linux 使用。两者的不同在于 CentOS 并不包含封闭源代码软件。

    239 引用 • 224 回帖
  • 前端

    前端技术一般分为前端设计和前端开发,前端设计可以理解为网站的视觉设计,前端开发则是网站的前台代码实现,包括 HTML、CSS 以及 JavaScript 等。

    246 引用 • 1338 回帖 • 1 关注
  • Tomcat

    Tomcat 最早是由 Sun Microsystems 开发的一个 Servlet 容器,在 1999 年被捐献给 ASF(Apache Software Foundation),隶属于 Jakarta 项目,现在已经独立为一个顶级项目。Tomcat 主要实现了 JavaEE 中的 Servlet、JSP 规范,同时也提供 HTTP 服务,是市场上非常流行的 Java Web 容器。

    162 引用 • 529 回帖 • 2 关注
  • Pipe

    Pipe 是一款小而美的开源博客平台。Pipe 有着非常活跃的社区,可将文章作为帖子推送到社区,来自社区的回帖将作为博客评论进行联动(具体细节请浏览 B3log 构思 - 分布式社区网络)。

    这是一种全新的网络社区体验,让热爱记录和分享的你不再感到孤单!

    133 引用 • 1124 回帖 • 112 关注
  • Hibernate

    Hibernate 是一个开放源代码的对象关系映射框架,它对 JDBC 进行了非常轻量级的对象封装,使得 Java 程序员可以随心所欲的使用对象编程思维来操纵数据库。

    39 引用 • 103 回帖 • 726 关注
  • DevOps

    DevOps(Development 和 Operations 的组合词)是一组过程、方法与系统的统称,用于促进开发(应用程序/软件工程)、技术运营和质量保障(QA)部门之间的沟通、协作与整合。

    58 引用 • 25 回帖 • 2 关注
  • 设计模式

    设计模式(Design pattern)代表了最佳的实践,通常被有经验的面向对象的软件开发人员所采用。设计模式是软件开发人员在软件开发过程中面临的一般问题的解决方案。这些解决方案是众多软件开发人员经过相当长的一段时间的试验和错误总结出来的。

    200 引用 • 120 回帖