APP下载

深入研究Java的类加载机制

2010-03-22张洪波

唐山师范学院学报 2010年2期
关键词:调用实例内存

赵 宇,张洪波

(唐山师范学院 计算机科学系,河北 唐山 063000)

在许多传统语言中,程序是作为启动(startup)过程的一部分立刻被加载的。然后是初始化,紧接着程序开始运行。这些语言的初始化过程必须小心控制,以确保static的初始化顺序不会造成麻烦。例如,如果某个static在另一个static被初始化之前就可以被有效地使用,那么 C++就会出现问题。Java就不会出现这个问题,因为它采用了一种不同的加载方式。由于 Java中的所有事物都是对象,所以许多动作就变得更加容易,加载动作仅仅是其中之一。每个类的编译代码都存在于它自己的独立的文件中。

一般来说,“类的代码在初次使用时才加载”这通常是指知道类的第一个对象被构建时才发生加载,但是当访问static数据成员或是static方法时,也会发生加载。初次使用之处也是静态初始化(static初始化)发生之处。所有的static对象和static代码段都会在加载时依程序中的顺序(即定义类时的书写顺序)依次初始化。当然,static只会被初始化一次。

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

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语言的设计初衷就是面向嵌入式领域的。

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(Stringargs[]){

try{

Classc=Class.forName("TestClass");

TestClassobject=(TestClass)c.newInstance();object.method();

}catch(Exceptione){

e.printStackTrace();

}

}

}

通过 Class类的 forName(Strings)方法把自定义类TestClass加载进来,并通过 newInstance()方法把实例初始化。Class的 forName()方法还有另外一种形式:ClassforName(String s,Boolean flag,ClassLoader classloader),s表示需要加载类的名称,flag表示在调用该函数加载类的时候是否初始化静态区,classloader表示加载该类所需的加载器。

forName(String s)是默认通过

ClassLoader.getCallerClassLoader()调用类加载器的,但是该方法是私有方法,我们无法调用,如果我们想使用

ClassforName(String s,Boolean flag,ClassLoader classloader)来加载类的话,就必须要指定类加载器,可以通过如下的方式来实现:

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

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

//获取test的类装载器;

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

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

3 自定义类加载机制

之前两种类加载方式都是调用系统的类加载器来实现加载的,其实也可以由程序员自己定义类加载器的。利用Java提供的java.net.URLClassLoader类就可以实现,代码如下:

try{

URLurl=newURL("file:/d:/test/lib/");

URLClassLoaderurlCL=newURLClassLoader(newURL[]{url});

Classc=urlCL.loadClass("TestClassA");

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

}catch(Exceptione){e.printStackTrace();}

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

4 类加载器的阶层体系

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

猜你喜欢

调用实例内存
核电项目物项调用管理的应用研究
笔记本内存已经在涨价了,但幅度不大,升级扩容无须等待
“春夏秋冬”的内存
LabWindows/CVI下基于ActiveX技术的Excel调用
基于系统调用的恶意软件检测技术研究
内存搭配DDR4、DDR3L还是DDR3?
完形填空Ⅱ
完形填空Ⅰ
利用RFC技术实现SAP系统接口通信
上网本为什么只有1GB?