暂无图片
暂无图片
暂无图片
暂无图片
暂无图片

水煮Java虚拟机 - ClassLoader源码分析(一)

然笑 2021-12-06
701


1、概述

在上一篇文章中,已经对 ClassLoader
的基本概念以及分类进行了详细的介绍,具体内容可以查看:水煮Java虚拟机 - 类加载器ClassLoader分析
本文主要对 ClassLoader
的源码进行简要分析,由于 ClassLoader
相关的源码比较多,内容比较长,将分为4篇文章进行介绍,选取的源码基于JDK1.8版本,涉及到的类如下:
  • java.lang.ClassLoader
  • java.security.SecureClassLoader
  • java.net.URLClassLoader
  • sun.misc.Launcher
  • sun.misc.Launcher$AppClassLoader
  • sun.misc.Launcher$ExtClassLoader


各个类之间的UML关系图如下:


2、ClassLoader

java.lang.ClassLoader
是一抽象类,是所有Java类加载器的父类(不包括启动类加载器,Bootstrap ClassLoader
是由C++语言实现的),整个类的源码相对较多,接下来会按照 核心属性、构造方法和其他方法逐一进行分析。


2.1 核心属性

/**
* 委派的父类加载器
*/
private final ClassLoader parent;
/**
* 类名对应的锁对象集合,当前类加载器具有并行能力时,将类名映射到相应的锁对象
* VM会根据这个字段来判断当前类加载器是否具有并行能力
*/
private final ConcurrentHashMap<String, Object> parallelLockMap;
/**
* 包名映射到 Certificate 的哈希集合
*/
private final Map <String, Certificate[]> package2certs;
/**
* 无签名类共享的证书数组
*/
private static final Certificate[] nocerts = new Certificate[0];
/**
* 记录当前类加载器加载的所有类,用于防止类被GC
*/
private final Vector<Class<?>> classes = new Vector<>();
/**
* 默认的权限域
*/
private final ProtectionDomain defaultDomain =
new ProtectionDomain(new CodeSource(null, (Certificate[]) null),
null, this, null);
/**
* 当前类加载器加载的所有类对应的权限域
*/
private final Set<ProtectionDomain> domains;
/**
* 当前类加载器定义的包对象集合,每个包会被映射成一个 Package 对象
*/
private final HashMap<String, Package> packages = new HashMap<>();
/**
* 系统类加载器
*/
private static ClassLoader scl;
/**
* 系统类加载器是否已设置标志
*/
private static boolean sclSet;
/**
* 所有加载的本地库名称集合
*/
private static Vector<String> loadedLibraryNames = new Vector<>();
/**
* 属于系统类的本地库集合
*/
private static Vector<NativeLibrary> systemNativeLibraries
= new Vector<>();
/**
* 与类加载器关联的本地库集合
*/
private Vector<NativeLibrary> nativeLibraries = new Vector<>();
/**
* 正在被加载或卸载的本地库集合
*/
private static Stack<NativeLibrary> nativeLibraryContext = new Stack<>();
/**
* 用户搜索路径
*/
private static String usr_paths[];
/**
* 系统搜索路径
*/
private static String sys_paths[];
/**
* 断言锁对象
*/
final Object assertionLock;
/**
* 默认的断言检查状态
*/
private boolean defaultAssertionStatus = false;
/**
* 包名映射到布尔型的断言状态,如果该字段为null,则将断言状态查询委托给VM
*/
private Map<String, Boolean> packageAssertionStatus = null;
/**
* 类全限名映射到布尔型的断言状态,如果该字段为null,则将断言状态查询委托给VM
*/
Map<String, Boolean> classAssertionStatus = null;


2.2 构造方法

private ClassLoader(Void unused, ClassLoader parent) {
// 设置父类加载器
this.parent = parent;
// 判断该类加载器是否具有并行能力
if (ParallelLoaders.isRegistered(this.getClass())) {
parallelLockMap = new ConcurrentHashMap<>();
package2certs = new ConcurrentHashMap<>();
domains =
Collections.synchronizedSet(new HashSet<ProtectionDomain>());
assertionLock = new Object();
} else {
// no finer-grained lock; lock on the classloader instance
// 无并行能力
// 并行锁对象集合为null
parallelLockMap = null;
package2certs = new Hashtable<>();
domains = new HashSet<>();
assertionLock = this;
}
}


protected ClassLoader(ClassLoader parent) {
this(checkCreateClassLoader(), parent);
}


protected ClassLoader() {
this(checkCreateClassLoader(), getSystemClassLoader());
}
  • ParallelLoaders
    ClassLoader
    内部静态类,其源码如下:
    private static class ParallelLoaders {
    private ParallelLoaders() {}


    // the set of parallel capable loader types
    // 具有并行能力的加载器类型集合
    private static final Set<Class<? extends ClassLoader>> loaderTypes =
    Collections.newSetFromMap(
    new WeakHashMap<Class<? extends ClassLoader>, Boolean>());
    static {
    synchronized (loaderTypes) {
    // 添加 ClassLoader 类
    loaderTypes.add(ClassLoader.class);
    }
    }


    /**
    * Registers the given class loader type as parallel capabale.
    * Returns {@code true} is successfully registered; {@code false} if
    * loader's super class is not registered.
    */
    static boolean register(Class<? extends ClassLoader> c) {
    synchronized (loaderTypes) {
    // 判断父类是否具有并行能力
    if (loaderTypes.contains(c.getSuperclass())) {
    // register the class loader as parallel capable
    // if and only if all of its super classes are.
    // Note: given current classloading sequence, if
    // the immediate super class is parallel capable,
    // all the super classes higher up must be too.
    // 当父类具有并行能力时,将 c 添加到 loaderTypes 中
    loaderTypes.add(c);
    return true;
    } else {
    return false;
    }
    }
    }


    /**
    * Returns {@code true} if the given class loader type is
    * registered as parallel capable.
    */
    static boolean isRegistered(Class<? extends ClassLoader> c) {
    // 判断类加载器是否已注册为并行类加载器
    synchronized (loaderTypes) {
    return loaderTypes.contains(c);
    }
    }
    }
  • checkCreateClassLoader()
    方法用于判断调用线程是否具有创建新的类加载器的权限,它会调用 java.lang.SecurityManager.checkCreateClassLoader()
    方法,如下:
    private static Void checkCreateClassLoader() {
    SecurityManager security = System.getSecurityManager();
    if (security != null) {
    // 调用 java.lang.SecurityManager.checkCreateClassLoader() 方法
    security.checkCreateClassLoader();
    }
    return null;
    }
  • getSystemClassLoader()
    方法用于获取系统类加载器,如下:
    @CallerSensitive
    public static ClassLoader getSystemClassLoader() {
    // 初始化系统类加载器
    initSystemClassLoader();
    if (scl == null) {
    return null;
    }
    // 创建 SecurityManager 对象
    SecurityManager sm = System.getSecurityManager();
    if (sm != null) {
    // 判断类加载器权限
    checkClassLoaderPermission(scl, Reflection.getCallerClass());
    }
    return scl;
    }


    private static synchronized void initSystemClassLoader() {
    // 如果系统类加载器未设置
    if (!sclSet) {
    if (scl != null)
    throw new IllegalStateException("recursive invocation");
    // 创建 Launcher 对象
    sun.misc.Launcher l = sun.misc.Launcher.getLauncher();
    if (l != null) {
    Throwable oops = null;
    // 从 Launcher 中获取 ClassLoader,并赋值给 scl
    scl = l.getClassLoader();
    try {
    // 赋予系统类加载器 scl 特权
    scl = AccessController.doPrivileged(
    new SystemClassLoaderAction(scl));
    } catch (PrivilegedActionException pae) {
    oops = pae.getCause();
    if (oops instanceof InvocationTargetException) {
    oops = oops.getCause();
    }
    }
    if (oops != null) {
    if (oops instanceof Error) {
    throw (Error) oops;
    } else {
    // wrap the exception
    throw new Error(oops);
    }
    }
    }
    sclSet = true;
    }
    }


2.3 loadClass

#loadClass(String name, boolean resolve)
方法负责加载指定名称的类,代码如下:
/**
* 加载指定名称的类
*
* @param name 类名称
* @param resolve 是否需要解析
* @return Class 对象
* @throws ClassNotFoundException
*/
protected Class<?> loadClass(String name, boolean resolve)
throws ClassNotFoundException
{
// 获取指定类名对应的锁对象
synchronized (getClassLoadingLock(name)) {
// First, check if the class has already been loaded
// 判断类是否已被加载
Class<?> c = findLoadedClass(name);
// 若类没有加载过
if (c == null) {
long t0 = System.nanoTime();
try {
// 判断父类加载器是否为null
if (parent != null) {
// 父类加载器不为null,则委托给父类加载器加载
c = parent.loadClass(name, false);
} else {
// 父类加载器为null,则委托给启动类加载器进行加载
c = findBootstrapClassOrNull(name);
}
} catch (ClassNotFoundException e) {
// ClassNotFoundException thrown if class not found
// from the non-null parent class loader
}


// 若没有找到,则调用 findClass 方法查找此类
if (c == null) {
// If still not found, then invoke findClass in order
// to find the class.
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();
}
}
// 判断是否类需要解析,若需要解析,则调用 resolveClass 方法
if (resolve) {
// 调用本地 resolveClass0 方法
resolveClass(c);
}
return c;
}
}


public Class<?> loadClass(String name) throws ClassNotFoundException {
return loadClass(name, false);
}
  • #getClassLoadingLock(String className)
    方法是为了获取加载类对应的锁对象,其实现如下:
    protected Object getClassLoadingLock(String className) {
    // 锁对象为 ClassLoader 本身
    Object lock = this;
    // 如果 parallelLockMap 不为空
    if (parallelLockMap != null) {
    Object newLock = new Object();
    // 当className没有对应的值时,设置新值 newLock
    lock = parallelLockMap.putIfAbsent(className, newLock);
    if (lock == null) {
    lock = newLock;
    }
    }
    return lock;
    }


2.4 findLoadedClass

#findLoadedClass(String name)
方法,用于从当前 ClassLoader
实例对象的缓存中查找已加载的类,调用的是native方法,源码如下:
protected final Class<?> findLoadedClass(String name) {
if (!checkName(name))
return null;
// 调用本地方法
return findLoadedClass0(name);
}


private native final Class<?> findLoadedClass0(String name);
  • #checkName(String name)
    方法用于检查类名是否为空或者是有效的二进制名称,代码如下:
    private boolean checkName(String name) {
    if ((name == null) || (name.length() == 0))
    return true;
    if ((name.indexOf('/') != -1)
    || (!VM.allowArraySyntax() && (name.charAt(0) == '[')))
    return false;
    return true;
    }


2.5 findBootstrapClassOrNull

#findBootstrapClassOrNull(String name)
方法,用于根据类名获取被启动类加载器加载的类,调用的是native方法,如下:
private Class<?> findBootstrapClassOrNull(String name)
{
// 检查类名是否符合规范
if (!checkName(name)) return null;
// 调用本地方法
return findBootstrapClass(name);
}


// return null if not found
private native Class<?> findBootstrapClass(String name);


2.6 findClass

#findClass(String name)
方法没有任何实现,它应该被类加载器的实现重写,该实现按照委托模型来加载类,如下:
protected Class<?> findClass(String name) throws ClassNotFoundException {
throw new ClassNotFoundException(name);
}
  • 若是找不到相应类,则直接抛出 ClassNotFoundException
    异常;


2.7 findSystemClass

#findSystemClass(String name)
方法负责从系统类加载器中查找指定类,若找不到,则从启动类加载器中查找,如下:
protected final Class<?> findSystemClass(String name)
throws ClassNotFoundException
{
// 获取系统类加载器
ClassLoader system = getSystemClassLoader();
// 若系统类加载器为空
if (system == null) {
// 判断类名是否符合规范
if (!checkName(name))
throw new ClassNotFoundException(name);
// 从启动类加载器中查找指定类
Class<?> cls = findBootstrapClass(name);
if (cls == null) {
// 未找到,直接抛出 ClassNotFoundException 异常
throw new ClassNotFoundException(name);
}
return cls;
}
// 从系统类加载器中查找指定类
return system.loadClass(name);
}

(未完待续)由于篇幅有限,关于 ClassLoader 剩下的方法将在下一篇文章进行分析,具体有:

  • defineClass

  • resolveClass

  • getResource

  • definePackage


参考资料

【1】ClassLoader 源码分析  

【2】Java 安全模型介绍  

【3】分布式Java应用基础与实践  

【4】《jdk8u源码分析》sun.misc.Launcher


文章转载自然笑,如果涉嫌侵权,请发送邮件至:contact@modb.pro进行举报,并提供相关证据,一经查实,墨天轮将立刻删除相关内容。

评论