java-基础-类加载
类加载
一. ClassLoader
默认提供三个ClassLoader:
启动(Bootstrap)类加载器:引导类装入器是用本地代码实现的类装入器,它负责将 <Java_Runtime_Home>/lib下面的核心类库或-Xbootclasspath选项指定的jar包加载到内存中。c实现,无法引用,作为jvm一部分;
扩展(Extension)类加载器:扩展类加载器是由Sun的ExtClassLoader(sun.misc.Launcher$ExtClassLoader)实现的。它负责将< Java_Runtime_Home >/lib/ext或者由系统变量-Djava.ext.dir指定位置中的类库加载到内存中。开发者可以直接使用标准扩展类加载器。
系统(System)类加载器:系统类加载器是由 Sun的 AppClassLoader(sun.misc.Launcher$AppClassLoader)实现的。它负责将系统类路径java -classpath或-Djava.class.path变量所指的目录下的类库加载到内存中。开发者可以直接使用系统类加载器。
自定义
自定义的ClassLoader都必须继承自java.lang.ClassLoader类
Extension和App也是继承自ClassLoader
但是Bootstrap不是继承自ClassLoader,它已经嵌入JVM内核中,底层由C编写
当JVM启动后,Bootstrap ClassLoader也随着启动,负责加载完核心类库后,并构造Extension ClassLoader和App ClassLoader类加载器。
二. 载类的原理
双亲委托模型
每个ClassLoader实例都有一个父类加载器的引用,这父类不是继承关系,是包含关系;
首先将加载任务委托给父类加载器,依次递归,如果父类加载器可以完成类加载任务,就成功返回;只有父类加载器无法完成此加载任务时,才自己去加载。
总体顺序如下:
Bootstrap ->Extension ->App ->返回给委托的发起者,由它到指定的文件系统或网络等URL中加载该类
如果没找到抛出异常
找到之后将它加载到内存当中,最后返回这个类在内存中的Class实例对象;
好处:避免重复加载,比如String永远都不可能自己加载
JVM如何判定两个类相同
两个方面:
- 类名
- 由哪个class loader加载
不同类加载器加载的,无法进行类型转换,属于不同的类型
三. loadClass
创建
创建时,第二个参数由getSystemClassLoader()生成
首先检查这个loader是否属于ParallelLoaders,如果在的话,直接使用并行loaders的配置,如果不在,需要自定义一些内容;
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
parallelLockMap = null;
package2certs = new Hashtable<>();
domains = new HashSet<>();
assertionLock = this;
}
}
加载
传入类的名字,第二个为是否解析这个类,这里用false,解析是native方法
类可以并行加载,不同的类都有一个锁,所以使用getClassLoadingLock(name)获取到锁
先查看这个类是否已经被加载,如果被加载直接返回;
如果parent非空,调用parent的loadClass方法,这就是双亲委派模型;
一层一层找上去,直到
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) {
//执行parent的loadclass方法
c = parent.loadClass(name, false);
} else {
//Bootstrap顶层了,加载自己的方法
c = findBootstrapClassOrNull(name);
}
} catch (ClassNotFoundException e) {
}
if (c == null) {
long t1 = System.nanoTime();
c = findClass(name);
sun.misc.PerfCounter.getParentDelegationTime().addTime(t1 - t0);
sun.misc.PerfCounter.getFindClassTime().addElapsedTimeFrom(t1);
sun.misc.PerfCounter.getFindClasses().increment();
}
}
if (resolve) {
resolveClass(c);
}
return c;
}
}
系统类加载器加载
三种ClassLoader都是定义在Launcher中的内部类,Launcher是用来运行main函数的类
构建自定义加载器时,继承了ClassLoader
其panrent由getSystemClassLoader()方法生成,那么它的parent是谁呢?
getSystemClassLoader方法就是调用initSystemClassLoader获取初始化的系统加载器,然后返回
public static ClassLoader getSystemClassLoader() {
initSystemClassLoader();
if (scl == null) {
return null;
}
SecurityManager sm = System.getSecurityManager();
if (sm != null) {
checkClassLoaderPermission(scl, Reflection.getCallerClass());
}
return scl;
}
系统加载器没有初始化时sclSet为false,执行第一个if
调用Launcher的getLauncher获取Launcher,这个Lancher是全局单例,Lancer的getClassLoader方法获取loader
private static synchronized void initSystemClassLoader() {
if (!sclSet) {
if (scl != null)
throw new IllegalStateException("recursive invocation");
sun.misc.Launcher l = sun.misc.Launcher.getLauncher();
if (l != null) {
Throwable oops = null;
scl = l.getClassLoader();
try {
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;
}
}
Lancher的这个loader是多少呢?Lancher构造如下
public Launcher() {
//外部
ClassLoader extcl;
try {
extcl = ExtClassLoader.getExtClassLoader();
} catch (IOException e) {
throw new InternalError(
"Could not create extension class loader", e);
}
// app
try {
loader = AppClassLoader.getAppClassLoader(extcl);
} catch (IOException e) {
throw new InternalError(
"Could not create application class loader", e);
}
// 线程的ContextClassLoader
Thread.currentThread().setContextClassLoader(loader);
// 如果需要安装到SecurityManager中
String s = System.getProperty("java.security.manager");
if (s != null) {
SecurityManager sm = null;
if ("".equals(s) || "default".equals(s)) {
sm = new java.lang.SecurityManager();
} else {
try {
sm = (SecurityManager)loader.loadClass(s).newInstance();
} catch (IllegalAccessException e) {
} catch (InstantiationException e) {
} catch (ClassNotFoundException e) {
} catch (ClassCastException e) {
}
}
if (sm != null) {
System.setSecurityManager(sm);
} else {
throw new InternalError(
"Could not create SecurityManager: " + s);
}
}
}
可看出loader是AppClassLoader
ExtClassLoader
Launcher中首先创建了ExtClassLoader
ExtClassLoader的getExtClassLoader如下
public static ExtClassLoader getExtClassLoader() throws IOException
{
final File[] dirs = getExtDirs();
try {
return AccessController.doPrivileged(
new PrivilegedExceptionAction<ExtClassLoader>() {
public ExtClassLoader run() throws IOException {
int len = dirs.length;
for (int i = 0; i < len; i++) {
MetaIndex.registerDirectory(dirs[i]);
}
return new ExtClassLoader(dirs);
}
});
} catch (java.security.PrivilegedActionException e) {
throw (IOException) e.getException();
}
ExtClassLoader最后初始化的父类构造
第三个传入的是URLStreamHandlerFactory
parent传入的是null
public URLClassLoader(URL[] urls, ClassLoader parent,
URLStreamHandlerFactory factory) {
super(parent);
// this is to make the stack depth consistent with 1.1
SecurityManager security = System.getSecurityManager();
if (security != null) {
security.checkCreateClassLoader();
}
ucp = new URLClassPath(urls, factory);
acc = AccessController.getContext();
}
父类SecureClassLoader构造时Parent也为空,再父类ClassLoader的parent也为空?
ExtClassLoader并没有找到父类!
再写一次loadClass,ExtClassLoader没有parent,但是在loadClass中会首先找到Bootstrap
//这个调用本地方法,尝试通过Bootstrap加载
c = findBootstrapClassOrNull(name);
if (c == null) {
long t1 = System.nanoTime();
c = findClass(name);
sun.misc.PerfCounter.getParentDelegationTime().addTime(t1 - t0);
sun.misc.PerfCounter.getFindClassTime().addElapsedTimeFrom(t1);
sun.misc.PerfCounter.getFindClasses().increment();
}
findClass有区别
APP加载器的父类URLClassLoader的如下
protected Class<?> findClass(final String name)
throws ClassNotFoundException {
try {
return AccessController.doPrivileged(
new PrivilegedExceptionAction<Class<?>>() {
public Class<?> run() throws ClassNotFoundException {
String path = name.replace('.', '/').concat(".class");
Resource res = ucp.getResource(path, false);
if (res != null) {
try {
return defineClass(name, res);
} catch (IOException e) {
throw new ClassNotFoundException(name, e);
}
} else {
throw new ClassNotFoundException(name);
}
}
}, acc);
} catch (java.security.PrivilegedActionException pae) {
throw (ClassNotFoundException) pae.getException();
}
这里的ucp是构建时生成的
URLClassLoader(URL[] urls, ClassLoader parent,
AccessControlContext acc) {
super(parent);
// this is to make the stack depth consistent with 1.1
SecurityManager security = System.getSecurityManager();
if (security != null) {
security.checkCreateClassLoader();
}
ucp = new URLClassPath(urls);
this.acc = acc;
}
这个urls是在创建App类加载器时获取的
...
return AccessController.doPrivileged(
new PrivilegedAction<AppClassLoader>() {
public AppClassLoader run() {
URL[] urls =
(s == null) ? new URL[0] : pathToURLs(path);
return new AppClassLoader(urls, extcl);
}
});
三. 测试观察
前两种方法加载
public class Main {
public static void main(String[] args) {
ClassLoader loader = Main.class.getClassLoader();
while(loader != null) {
System.out.println(loader);
loader = loader.getParent(); //获得父类加载器的引用
}
System.out.println(loader);
}
}
输出:
sun.misc.Launcher$AppClassLoader@75b84c92
sun.misc.Launcher$ExtClassLoader@1b6d3586
null
把Main.class打包,放在JAVA_HOME/jre/lib/ext目录下,重新运行
Bootstrcp ClassLoader加载
1、在jvm中添加-Xbootclasspath参数,指定Bootstrcp ClassLoader加载类的路径,并追加我们自已的jar(ClassTestLoader.jar)
2、将class文件放到JAVA_HOME/jre/classes/目录下(上面有提到)
四. 动态扩展方式
自定义ClassLoader
因为Java中提供的默认ClassLoader,只加载指定目录下的jar和class,如果我们想加载其它位置的类或jar时
比如:我要加载网络上的一个class文件,通过动态加载到内存之后,要调用这个类中的方法实现我的业务逻辑。
定义自已的类加载器分为两步:
- 继承java.lang.ClassLoader
- 重写父类的findClass方法
先获取.class的二进制码,存在数据组中
protected Class<?> findClass(String name) throws ClassNotFoundException {
Class clazz = null;
byte[] classData = getClassData(name);
if (classData == null) {
throw new ClassNotFoundException();
}
clazz = defineClass(name, classData, 0, classData.length);
return clazz;
}
调用defineClass把它构建成一个Class
protected final Class<?> defineClass(String name, byte[] b, int off, int len,
ProtectionDomain protectionDomain)
throws ClassFormatError
{
protectionDomain = preDefineClass(name, protectionDomain);
String source = defineClassSourceLocation(protectionDomain);
Class<?> c = defineClass1(name, b, off, len, protectionDomain, source);
postDefineClass(c, protectionDomain);
return c;
}
测试代码
MyLoader myLoader = new MyLoader();
Class clazz = myLoader.loadClass("ClassTest");
System.out.println(clazz.getClassLoader());
forName
五. forName过程
jdbc中,加载驱动,使用当前类的类加载器加载
Class.forName(DRIVER_NAME);
代码如下:
public static Class<?> forName(String className)
throws ClassNotFoundException {
//先获取哪个类调用了这个方法
Class<?> caller = Reflection.getCallerClass();
//获取这caller类的类加载器
return forName0(className, true, ClassLoader.getClassLoader(caller), caller);
}

浙公网安备 33010602011771号