`
kuru
  • 浏览: 136859 次
  • 性别: Icon_minigender_1
  • 来自: 广州
社区版块
存档分类
最新评论

深入探讨Java的类加载机制

    博客分类:
  • java
阅读更多

  Java 语言是一种具有动态性的解释型编程语言,当指定程序运行的时候, Java 虚拟机就将编译生成的 . class 文件按照需求和一定的规则加载进内存,并组织成为一个完整的 Java 应用程序。 Java 语言把每个单独的类 Class 和接口 Implements 编译成单独的一个 . class 文件,这些文件对于 Java 运行环境来说就是一个个可以动态加载的单元。正是因为 Java 的这种特性,我们可以在不重新编译其它代码的情况下,只编译需要修改的单元,并把修改文件编译后的 . class 文件放到 Java 的路径当中, 等到下次该 Java 虚拟机器重新激活时,这个逻辑上的 Java 应用程序就会因为加载了新修改的 .class 文件,自己的功能也做了更新,这就是 Java 的动态性。

    下面用一个简单的例子让大家对 Java 的动态加载有一个基本的认识:

class TestClassA{
public void method(){
  System.out.println("Loading ClassA");
}
}

public class ClassLoaderTest {
public static void main(String args[]){
  TestClassA testClassA = new TestClassA();
  testClassA.method();
}
}

编译后输入命令: java -verbose:class ClassLoaderTest ,执行文件。


从运行结果我们可以看到, JRE ( JavaRuntime Environment )首先加载 ClassLoaderTest 文件,然后再加载 TestClassA 文件,从而实现了动态加载。

    1. 预先加载与依需求加载
    Java 运行环境为了优化系统,提高程序的执行速度,在 JRE 运行的开始会将 Java 运行所需要的基本类采用预先加载( pre-loading )的方法全部加载要内存当中,因为这些单元在 Java 程序运行的过程当中经常要使用的,主要包括 JRE 的 rt.jar 文件里面所有的 .class 文件。

    当 java.exe 虚拟机开始运行以后,它会找到安装在机器上的 JRE 环境,然后把控制权交给 JRE , JRE 的类加载器会将 lib 目录下的 rt.jar 基础类别文件库加载进内存,这些文件是 Java 程序执行所必须的,所以系统在开始就将这些文件加载,避免以后的多次 IO 操作,从而提高程序执行效率。

    相对于预先加载,我们在程序中需要使用自己定义的类的时候就要使用依需求加载方法( load-on-demand ),就是在 Java 程序需要用到的时候再加载,以减少内存的消耗,因为 Java 语言的设计初衷就是面向嵌入式领域的。

    在这里还有一点需要说明的是, JRE 的依需求加载究竟是在什么时候把类加载进入内部的呢?

    我们在定义一个类实例的时候,比如 TestClassA testClassA ,这个时候 testClassA 的值为 null ,也就是说还没有初始化,没有调用 TestClassA 的构造函数,只有当执行 testClassA = new TestClassA() 以后, JRE 才正真把 TestClassA 加载进来。
2. 隐式加载和显示加载

    Java 的加载方式分为隐式加载( implicit )和显示加载( explicit ),上面的例子中就是用的隐式加载的方式。所谓隐式加载就是我们在程序中用 new 关键字来定义一个实例变量, JRE 在执行到 new 关键字的时候就会把对应的实例类加载进入内存。隐式加载的方法很常见,用的也很多, JRE 系统在后台自动的帮助用户加载,减少了用户的工作量,也增加了系统的安全性和程序的可读性。

    相对于隐式加载的就是我们不经常用到的显示加载。所谓显示加载就是有程序员自己写程序把需要的类加载到内存当中,下面我们看一段程序:

class TestClass{
public void method(){
  System.out.println("TestClass-method");
}
}

public class CLTest {
public static void main(String args[]) {
  try{
  Class c = Class.forName("TestClass");
  TestClass object = (TestClass)c.newInstance();
  object.method();
  }catch(Exception e){
  e.printStackTrace();
  }
}
}

    我们通过 Class 类的 forName (String s) 方法把自定义类 TestClass 加载进来,并通过 newInstance ()方法把实例初始化。事实上 Class 类还很多的功能,这里就不细讲了,有兴趣的可以参考 JDK 文档。

    Class 的 forName() 方法还有另外一种形式: Class forName(String s, boolean flag, ClassLoader classloader) , s 表示需要加载类的名称, flag 表示在调用该函数加载类的时候是否初始化静态区, classloader 表示加载该类所需的加载器。

    forName (String s) 是默认通过 ClassLoader.getCallerClassLoader() 调用类加载器的,但是该方法是私有方法,我们无法调用,如果我们想使用 Class forName(String s, boolean flag, ClassLoader classloader) 来加载类的话,就必须要指定类加载器,可以通过如下的方式来实现:

Test test = new Test();//Test 类为自定义的一个测试类;

ClassLoader cl = test. getClass().getClassLoader();

  // 获取 test 的类装载器;

Class c = Class.forName("TestClass", true, cl);

    因为一个类要加载就必需要有加载器,这里我们是通过获取加载 Test 类的加载器 cl 当作加载 TestClass 的类加载器来实现加载的。
   3. 自定义类加载机制

    之前我们都是调用系统的类加载器来实现加载的,其实我们是可以自己定义类加载器的。利用 Java 提供的 java.net.URLClassLoader 类就可以实现。下面我们看一段范例:

  try{

  URL url = new URL("file:/d:/test/lib/");

  URLClassLoader urlCL = new URLClassLoader(new URL[]{url});

  Class c = urlCL.loadClass("TestClassA");

  TestClassA object = (TestClassA)c.newInstance();

  object.method();

  }catch(Exception e){

  e.printStackTrace();

  }

    我们通过自定义的类加载器实现了 TestClassA 类的加载并调用 method ()方法。分析一下这个程序:首先定义 URL 指定类加载器从何处加载类, URL 可以指向网际网络上的任何位置,也可以指向我们计算机里的文件系统 ( 包含 JAR 文件 ) .上述范例当中我们从 file:/d:/test/lib/ 处寻找类;然后定义 URLClassLoader 来加载所需的类,最后即可使用该实例了。

    4. 类加载器的阶层体系

    讨论了这么多以后,接下来我们仔细研究一下 Java 的类加载器的工作原理:

    当执行 java ***.class 的时候, java.exe 会帮助我们找到 JRE ,接着找到位于 JRE 内部的 jvm.dll ,这才是真正的 Java 虚拟机器 , 最后加载动态库,激活 Java 虚拟机器。虚拟机器激活以后,会先做一些初始化的动作,比如说读取系统参数等。一旦初始化动作完成之后,就会产生第一个类加载器―― Bootstrap Loader , Bootstrap Loader 是由 C++ 所撰写而成,这个 Bootstrap Loader 所做的初始工作中,除了一些基本的初始化动作之外,最重要的就是加载 Launcher.java 之中的 ExtClassLoader ,并设定其 Parent 为 null ,代表其父加载器为 BootstrapLoader .然后 Bootstrap Loader 再要求加载 Launcher.java 之中的 AppClassLoader ,并设定其 Parent 为之前产生的 ExtClassLoader 实体。这两个加载器都是以静态类的形式存在的。这里要请大家注意的是, Launcher$ExtClassLoader.class 与 Launcher$AppClassLoader.class 都是由 Bootstrap Loader 所加载,所以 Parent 和由哪个类加载器加载没有关系。

这三个加载器就构成我们的 Java 类加载体系。他们分别从以下的路径寻找程序所需要的类:

BootstrapLoader : sun.boot.class.path

ExtClassLoader: java.ext.dirs

AppClassLoader: java.class.path

    这三个系统参量可以通过 System.getProperty() 函数得到具体对应的路径。大家可以自己编程实现查看具体的路径。
分享到:
评论
1 楼 kufish001 2009-02-24  
引用
    当执行 java ***.class 的时候, java.exe 会帮助我们找到 JRE ,接着找到位于 JRE 内部的 jvm.dll ,这才是真正的 Java 虚拟机器 , 最后加载动态库,激活 Java 虚拟机器。虚拟机器激活以后,会先做一些初始化的动作,比如说读取系统参数等。一旦初始化动作完成之后,就会产生第一个类加载器―― Bootstrap Loader , Bootstrap Loader 是由 C++ 所撰写而成,这个 Bootstrap Loader 所做的初始工作中,除了一些基本的初始化动作之外,最重要的就是加载 Launcher.java 之中的 ExtClassLoader ,并设定其 Parent 为 null ,代表其父加载器为 BootstrapLoader .然后 Bootstrap Loader 再要求加载 Launcher.java 之中的 AppClassLoader ,并设定其 Parent 为之前产生的 ExtClassLoader 实体。这两个加载器都是以静态类的形式存在的。这里要请大家注意的是, Launcher$ExtClassLoader.class 与 Launcher$AppClassLoader.class 都是由 Bootstrap Loader 所加载,所以 Parent 和由哪个类加载器加载没有关系。

请问你是怎么知道Bootstrap Loader是由C++写的?我很想知道

相关推荐

    深入理解java类加载机制

    在类加载方面,我们将深入探讨Java程序的类加载原理和流程,包括加载、验证、准备、解析和初始化等五个环节的详细解析,并对其强调点进行详细讲解。我们将详细介绍Java虚拟机中类的生命周期并探讨类加载时的各种问题...

    Java的类加载机制

    深入探讨Java的类加载机制.mht

    Java类加载内幕

    类加载是java语言提供的最强大的机制之一。...然后深入探讨java虚拟机(JVM)是怎么利用类加载器读取代码,及java中类加载器的主要类型。接着用一个类加载的基本算法看一下类加载器怎么加载一个内部类。本文的下一节演

    JAVA基础课程讲义

    静态初始化块(经常用来初始化类,加载类信息时执行!) 67 package 68 JDK中的主要包 68 import 68 eclipse的使用 69 继承(extend, inheritance) 70 为什么需要继承?继承的作用? 70 继承介绍 70 如何实现继承? ...

    深入理解Android:卷I--详细书签版

    第9章对vold和rild的原理和机制进行了深入的分析,同时还探讨了 phone设计优化的问题;第10章分析了多媒体系统中mediascanner的工作原理。  本书适合有一定基础的android应用开发工程师和系统工程师阅读。通过对...

    深入解析Spring IoC:源码与实践指南

    本文深入探讨了Spring IoC容器的加载过程及其源码实现,揭示了Spring中最为根本的概念之一。这包括从AnnotationConfigApplicationContext的实例化开始,到DefaultListableBeanFactory工厂的建立,再到...

    Java虚拟机

    第三部分分析了虚拟机的执行子系统,包括类文件结构、虚拟机类加载机制、虚拟机字节码执行引擎。第四部分讲解了程序的编译与代码的优化,阐述了泛型、自动装箱拆箱、条件编译等语法糖的原理;讲解了虚拟机的热点探测...

    Java数据编程指南

    在相互的基础上,前四部分深入广泛地探讨了各种Java技术,它们可以组合或单独用于为各种数据访问与处理情况开发实际方案。第五部分中的案例研究包括了一些实际的围绕数据的应用程序范例。 本书只需要读者具有Java...

    深入理解Android卷1全

    第5章 深入理解常见类 / 95 5.1 概述 / 96 5.2 以“三板斧”揭秘RefBase、sp和wp / 96 5.2.1 第一板斧——初识影子对象 / 96 5.2.2 第二板斧——由弱生强 / 103 5.2.3 第三板斧——破解生死魔咒 / 106 5.2.4 轻量级...

    《深入理解Android》卷Ⅰ

    第5章 深入理解常见类 5.1 概述 5.2 以“三板斧”揭秘RefBase、sp和wp 5.2.1 第一板斧--初识影子对象 5.2.2 第二板斧--由弱生强 5.2.3 第三板斧--破解生死魔咒 5.2.4 轻量级的引用计数控制类LightRefBase 5.2.5 题外...

    Android开发艺术探索.任玉刚(带详细书签).pdf

    本书是一本Android进阶类书籍,采用理论、源码和实践相结合的方式来阐述高水准的Android应用开发要点。本书从三个方面来组织内容。第一,介绍Android开发者不容易掌握的一些知识点;第二,结合Android源代码和应用层...

    Flex企业应用开发实战源代码

    接着剖析了Flex与Java的通信机制,以及Flex企业应用的客户端架构和服务器端架构;再接着详细讲解了BlazeDS框架的使用方法和工作原理,并通过迭代的方式完整地演示一个真实的Flex企业级应用的开发全过程,实战性极强...

    asp.net知识库

    asp.net 运行机制初探(httpModule加载) 利用反射来查看对象中的私有变量 关于反射中创建类型实例的两种方法 ASP.Net应用程序的多进程模型 NET委托:一个C#睡前故事 [推荐] - [原创] Microsoft .NET策略及框架概述 ...

Global site tag (gtag.js) - Google Analytics