|
JVM的体系结构
从图上可以看出,JVM体系结构中,主要有以下几个部分:
1.java class文件
2.class文件加载系统
3.运行期数据区(java栈、堆、方法区、PC寄存器、本地方法区)
4.执行引擎
5.本地方法接口
6.本地方法库
JVM的classLoader体系-1
命名空间:
?不同的类加载器具有不同的命名空间
?同一命名空间内,类的名称具有惟一性
?同一命名空间内,类之间可以直接交互
?不同命名空间之间,除非显示的提供交互的机制,是不能交互的
类加载器:
?启动类加载器(Bootstrap classloader)
?扩展类加载器(Extended classloader)
?系统类加载器(Application classloader)
?Bootstrap加载Extended和Application,Extended的父类为Bootstrap,由于Bootstrap是由C++语言编写,所以Extended的父类为null,Bootstrap在加载Application的时候,将其父类设置为Extended
每个类加载器所加载文件的目录:
?每个类加载器所加载文件的目录:
?可以通过方法:System.getProperty(String str);方法获得
?Bootstrap: sun.boot.class.path
?Extended: java.ext.dirs
?Application:java.class.path
类加载器的委派模型:
?类加载器总是委托其父类去加载所需要的文件
?如果一个文件A引用另一个文件B,在没有自定义加载B的加载器的情况下,B类只能由A类的加载器或者其父类加载
JVM的classLoader体系-2
在“JVM的classLoader体系-1”,总结了jvm的classloader一些基本的内容,肯定有很多不足的地方,还请大家指点,现在对jvm类加载机制进行一下测试:
1.建立测试文件Main.java和Test.java
Main.java负责打印出Main.java和Test.java的类加载器)
Main.java代码:
public class Main {
public static void main(String args[]){
System.out.print("Main's classLoader is ");
System.out.println(Main.class.getClassLoader());
System.out.print("Test's classLoader is ");
System.out.print(Test.class.getClassLoader());
}
}
Test.java(do nothing):
Test.java代码:
public class Test {
}
2.测试准备
在F盘下面建立一个javasource的文件夹,并将此文件夹加到CLASSPATH的value中,运行结果如下:
Main's classLoader is sun.misc.Launcher$AppClassLoader@19821f(Main.class的加载器)
Test's classLoader is sun.misc.Launcher$AppClassLoader@19821f(Test.class的加载器)
3.classLoader加载机制的测试:
3.1测试01-文件存放的位置
?Javasource目录下:
?Main.class
?Test.class
?Jdk\jre\ib\ext\classes目录下:
?Main.class
?Test.class
?Jdk\jre\classes目录下:
?Main.class
?Test.class
3.2测试01-结果
?Main's classLoader is null
?Test's classLoader is null
3.3测试01-结果分析
在运行java Main的时候,由Application ClassLoader加载所需要的类,在加载的过程中,Application ClassLoader首先将加载任务委派给其父类Extended ClassLoader,Extended ClassLoader又将加载的任务委派给Bootstrap ClassLoader,由于在Bootstrap ClassLoader下面有Main.class文件,所以BootStrap ClassLoader负责类Main的加载。在Main.class中,引用了Test.class,在默认的情况下,由加载Main.class的类加载器加载Test.class,所以Test.class的类加载器也是null
3.2测试02-文件存放的位置
?Javasource目录下:
?Main.class
?Test.class
?Jdk\jre\ib\ext\classes目录下:
?Main.class
?Test.class
?Jdk\jre\classes目录下:
?Main.class
3.2测试02-测试结果
?Main's classLoader is null
?Test's classLoader is Exception in thread "main" java.lang.NoClassDefFoundError:
Test at Main.main(Main.java:8)
3.2测试02-测试结果分析
由于Bootstrap ClassLoader下面有Main.class,所以由Bootstrap ClassLoader负责加载Main.class,在默认的情况下,Main.class所引用的类将由Bootstrap加载,由于Bootstrap所加载的目录下的Test.class被删除了,所以出来NoClassDefFoundError的现象
3.3测试03-文件存放的位置
?Javasource目录下:
?Main.class
?Test.class
?Jdk\jre\ib\ext\classes目录下:
?Main.class
?Test.class
?Jdk\jre\classes目录下:
?Test.class
3.3测试03-测试结果
?Main's classLoader is sun.misc.Launcher$ExtClassLoader@126b249
?Test's classLoader is null
3.3测试03-测试结果分析
Main.class由ExtClassLoader加载,在默认的情况下,Test.class也应该由ExtClassLoader加载,ExtClassLoader首先委派其父类Bootstrap ClassLoader加载,由于Bootstrap所加载文件下有Test.class,所以由Bootstrap负责加载Test.class
3.4测试04-文件存放的位置
?Javasource目录下:
?Main.class
?Test.class
?Jdk\jre\ib\ext\classes目录下:
?Main.class
?Test.class
?Jdk\jre\classes目录下:
3.4测试04-测试结果
?Main's classLoader is sun.misc.Launcher$ExtClassLoader@182f0db
?Test's classLoader is sun.misc.Launcher$ExtClassLoader@182f0db
3.4测试04-测试结果分析
思路如测试01
3.5测试05-文件存放的位置
?Javasource目录下:
?Main.class
?Test.class
?Jdk\jre\ib\ext\classes目录下:
?Main.class
?Jdk\jre\classes目录下:
3.5测试05-测试结果
Main's classLoader is sun.misc.Launcher$ExtClassLoader@126b249
Test's classLoader is Exception in thread "main" java.lang.NoClassDefFoundError:
Test
at Main.main(Main.java:8)
Caused by: java.lang.ClassNotFoundException: Test
at java.net.URLClassLoader$1.run(URLClassLoader.java:202)
at java.security.AccessController.doPrivileged(Native Method)
at java.net.URLClassLoader.findClass(URLClassLoader.java:190)
at sun.misc.Launcher$ExtClassLoader.findClass(Launcher.java:229)
at java.lang.ClassLoader.loadClass(ClassLoader.java:307)
at java.lang.ClassLoader.loadClass(ClassLoader.java:248)
... 1 more
3.5测试05-测试结果分析
思路如测试02
3.6测试06-文件存放的位置
?Javasource目录下:
?Main.class
?Test.class
?Jdk\jre\ib\ext\classes目录下:
?Test.class
?Jdk\jre\classes目录下:
3.6测试06-测试结果
?Main's classLoader is sun.misc.Launcher$AppClassLoader@ad3ba4
?Test's classLoader is sun.misc.Launcher$ExtClassLoader@126b249
3.6测试06-测试结果分析
思路如测试03
4.总结
1.从测试结果中可以看出,类加载器在加载应用程序的class文件时,首先委托其父类进行加载,在父类加载失败的情况下,从由自己去加载。
2.在默认情况下,类A引用类B,那么类B的加载由类A的加载器或者类A加载器的父类来完成
自定义ClassLoader
Customclassloader代码
import java.io.DataInputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.util.Hashtable;
public class CustomClassLoader extends ClassLoader {
/**
* 从来存放所加载的类
*/
private Hashtable<String, Class<?>> classes = new Hashtable<String, Class<?>>();
/**
* 要加载文件的目录
*/
private String rootDir;
/**
* 将此类加载器的父类设置为加载此类的类加载器
*/
public CustomClassLoader(String rootDir) {
super(CustomClassLoader.class.getClassLoader());
if (rootDir == null || rootDir.trim().equals("")) {
throw new IllegalArgumentException("您说输入的文件目录为空");
}
this.rootDir = rootDir;
}
/**
* 根据类名加载类文件
*/
public Class<?> loadClass(String className) throws ClassNotFoundException {
return findClass(className);
}
/**
* 根据类名查找类
*/
public Class<?> findClass(String className) {
byte classByte[];
Class<?> result = null;
result = (Class<?>) classes.get(className);
if (result == null) {
try {//首先通过系统类加载器加载
return findSystemClass(className);
} catch (Exception e) {
}
}else{
return result;
}
//通过自定义类加载器加载类文件
try {
String classPath = rootDir + "\\"
+ className.replace('.', File.separatorChar) + ".class";
classByte = loadClassData(classPath);
result = defineClass(className, classByte, 0, classByte.length,
null);
if(result==null){
throw new ClassNotFoundException();
}
classes.put(className, result);
return result;
} catch (Exception e) {
e.printStackTrace();
return null;
}
}
// 负责加载class文件的数据信息
private byte[] loadClassData(String className) throws IOException {
File f;
f = new File(className);
int size = (int) f.length();
byte buff[] = new byte[size];
FileInputStream fis = new FileInputStream(f);
DataInputStream dis = new DataInputStream(fis);
dis.readFully(buff);
dis.close();
return buff;
}
} |
|