您的位置:首页 > 其它

JVM之类加载器

2016-06-06 11:11 435 查看

JVM之类加载器

1.类加载器:通过一个类的全限定名称来获取描述此类的二进制字节流的过程,该动作在虚拟机的外部实现,目的是方便应用程序自己决定如何获取所需要的类,从Java虚拟机的角度只存在两种不同的类加载器:启动类加载器(C++语言实现),其它(Java语言实现,独立于虚拟机外部继承自抽象类java.lang.ClassLoader)。从开发人员角度,大部分Java程序会用到以下三种类加载器:
.启动类加载器,主要负责<JAVA_HOME>\bin目录下类的加载;
.扩展类加载器,负责加载<JAVA_HOME>\lib\etc 目录下类的加载;
.应用程序类加载器,负责加载用户类路径(ClassPath)上所指定的类库,一般为程序中默认的类加载器;
2.类的唯一性:任意一个Java类都需要由加载它的类加载器以及该类本身共同确定该类在JVM虚拟机中的唯一性,比较两个类是否相等的前提是由同一个类加载器的前提下。常见的equals(),isAssignableFrom(),isInstance(),instanceOf关键字等。可使用如下代码进行验证:
/*自定义类加载器*/
public class XClassLoader extends ClassLoader {

@Override
public Class<?> loadClass(String name) throws ClassNotFoundException {
try {
String fileName = name.substring(name.lastIndexOf('.') + 1) + ".class";
/* 将Class字节流转为输入流 */
InputStream is = getClass().getResourceAsStream(fileName);
if (is == null) {
return super.loadClass(name);
}
byte[] b = new byte[is.available()];
is.read(b);
return defineClass(name, b, 0, b.length);
} catch (IOException e) {
e.printStackTrace();
}
return super.loadClass(name);
}
/* 验证类的唯一性条件 */
public static void main(String[] args)
throws InstantiationException, IllegalAccessException, ClassNotFoundException {
XClassLoader classLoader = new XClassLoader();
Object obj = classLoader.loadClass("cn.com.xiaofen.Hello").newInstance();
System.out.println(obj.getClass());
System.out.println(obj instanceof cn.com.xiaofen.Hello);
}
}
3.双亲委派模型:为保证在JVM中同一个.class文件被不同的类加载器加载都是同一个Class对象。示意图如下:


 
.双亲委派模型要求除了顶层启动类加载器之外,其余的类加载器都应当有自己的父类加载器,此处的父子关系通常使用组合的关系来复用父加载器的代码。

.双亲委派模型并不是一个强制性的约束模型,而是Java设计者推荐给开发者的一种类加载器实现方式。

.工作流程,如果一个类加载器收到类加载请求,首先将请求委派给父类加载器去完成,每一个层次的类加载器都是如此,因此所有的类加载请求都应该传送到顶层的启动类加载器中,只有当父类加载器反馈自己无法完成类加载请求时,子类才会尝试自己去加载。

.双亲委派模型对于Java应用程序的稳定运作很重要,实现方式却很简单

protected Class<?> loadClass(String name, boolean resolve) throws ClassNotFoundException {
synchronized (getClassLoadingLock(name)) {
// 检查该类是否已经被加载过
Class<?> c = findLoadedClass(name);
if (c == null) {
long t0 = System.nanoTime();
try {
if (parent != null) {
/* 委托父类加载器 */
c = parent.loadClass(name, false);
} else {
/* 当前类加载器加载 */
c = findBootstrapClassOrNull(name);
}
} catch (ClassNotFoundException e) {
// ClassNotFoundException thrown if class not found
// from the non-null parent class loader
}

if (c == null) {
/* 任然没有发现调用本类的findClass 方法加载 */
long t1 = System.nanoTime();
c = findClass(name);

// this is the defining class loader; record the stats
sun.misc.PerfCounter.getParentDelegationTime().addTime(t1 - t0);
sun.misc.PerfCounter.getFindClassTime().addElapsedTimeFrom(t1);
sun.misc.PerfCounter.getFindClasses().increment();
}
}
if (resolve) {
resolveClass(c);
}
return c;
}
}
4.破坏双亲委派模型

.第一次被破坏,重写java.lang.ClassLoader的protected 方法loadClass()。

.第二次被破坏,发生在基础类(越基础的类越有顶层类加载器加载)调用回用户代码的场景中,启动类无法加载用户代码。Java中使用线程上下文类加载器进行设置,该加载器通过java.lang.Thread类的setContextClassLoader()方法进行设置,如果线程创建时候没有设置,将从父线程中继承一个,如果在应用程序范围内都没有设置过,则默认使用应用程序类加载器。会出现父类加载器调用子类加载器完成类加载的情况。

.第三次被破坏,用户对程序的动态性追求而导致,常见的:代码热替换、模块热部署等等。OSGI(Java动态化模块化系统的一系列规范)环境下,类加载器不再是双亲委派模型中的树状结构,而是更加负责的网状结构。

 

参考《深入理解Java虚拟机第二版》
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签:  JVM 类加载 jvm