Java虚拟机种装载:原理、实现与应用
一、引言
Java虚拟机(JVM)的类装载就是指将包含在类文件中的字节码装载到JVM中, 并使其成为JVM一部门的过程。
二、Java虚拟机的类装载完成与应用
2.1 装载过程简介
在Java中,类装载器把一个类装入Java虚拟机中,要经由三个步调来完成:装载、链接和初始化,其中链接又能够分为校验、预备和解析三步,除了解析外,其它步调是严厉依照递次完成的,各个步调的首要工作如下:
装载:查找和导入类或接口的二进制数据;
校验:搜检导入类或接口的二进制数据的准确性;
预备:给类的静态变量分派并初始化存储空间;
解析:将符号援用转成直接援用;
初始化:激活类的静态变量和静态Java代码块。
java虚拟机使用每一个类的第一件事情就是将该类的字节码装载进来,装载类字节码的功能是由类装载器完成的,类装载器负责根据一个类的名称来定位和生成类的字节码数据后返回给java虚拟机。最常见的类加载器是将要加载的类名转换成一个.class文件名,然后从文件系统中找到该文件并读取其中的内容,这种类装载器也不是直接将.class文件中的内容原封不动地返回给java虚拟机,它需要将.class文件中的内容转换成Java虚拟机使用的类字节码,譬如,Java程序中的字符串编译成.class文件后是以UTF-8编码存在的,而装载进java 虚拟机后要被转换成Unicode编码。
类加载的时候遵循一个原则:“类加载器会依类的继承体系从上至下依次加载”。举个例子:“如果C继承了B并实现了接口I,而B有继承自A”,则类加载器在加载C时,加载的次序会是A>B>I>C,(注:interface会如同class一样被Java编译器编译为独立的.class文件)
2.2 装载的完成
JVM中类的装载是由ClassLoader和它的子类SecureClassLoader来完成的,Java ClassLoader 是一个主要的Java运转时系统组件。它担任在运转时查找和装入类文件的类。在Java中,ClassLoader是一个抽象类,它在包java.lang中。
JVM在运行时会产生三个ClassLoader:Bootstrap ClassLoader、Extension ClassLoader和AppClassLoader。Bootstrap是用C++编写的,我们在Java中看不到它,是null,是JVM自带的类装载器,用来装载核心类库,如java.lang.*等。
AppClassLoader的Parent是ExtClassLoader,而ExtClassLoader的Parent为Bootstrap ClassLoader。
系统为什么要分别指定这么多的ClassLoader类呢?
答案在于因为java是动态加载类的,这样的话,可以节省内存,用到什么加载什么,就是这个道理,然而系统在运行的时候并不知道我们这个应用与需要加载些什么类,那么,就采用这种逐级加载的方式
(1)首先加载核心API,让系统最基本的运行起来
(2)加载扩展类
(3)加载用户自定义的类
2.3 装载的应用
普通来说,我们利用虚拟机的类装载时需要继承抽象类java.lang.ClassLoader,并重写loadClass()。下面我们举例申明它的应用。
public abstract class MultiClassLoader extends ClassLoader{ ... public synchronized Class loadClass(String s, boolean flag)throws ClassNotFoundException { /* 搜检类s能否曾经在当地内存*/ Class class1 = (Class)classes.get(s); /* 类s曾经在当地内存*/ if(class1 != null) return class1; /*用默许的ClassLoader 装入类*/ try { class1 = super.findSystemClass(s); return class1; } catch(ClassNotFoundException _ex) { System.out.println(">> Not a system class."); } /* 获得类s的字节数组*/ byte abyte0[] = loadClassBytes(s); if(abyte0 == null) throw new ClassNotFoundException(); /* 将类字节数组转换为类*/ class1 = defineClass(null, abyte0, 0, abyte0.length); if(class1 == null) throw new ClassFormatError(); if(flag) resolveClass(class1); /*解析类*/ /* 将新加载的类放入当地内存*/ classes.put(s, class1); System.out.println(">> Returning newly loaded class."); /* 返回已装载、解析的类*/ return class1; } ... }