[arthas@17048]$ classloader
name numberOfInstances loadedCountTotal
BootstrapClassLoader 1 2248
com.taobao.arthas.agent.ArthasClassloader 1 1351
sun.misc.Launcher$ExtClassLoader 1 47
sun.reflect.DelegatingClassLoader 15 15
sun.misc.Launcher$AppClassLoader 1 8
Affect(row-cnt:5) cost in 7 ms.
[arthas@17048]$ classloader -t
+-BootstrapClassLoader
+-sun.misc.Launcher$ExtClassLoader@7fe21cf8
+-com.taobao.arthas.agent.ArthasClassloader@53e59711
+-sun.misc.Launcher$AppClassLoader@18b4aac2
Affect(row-cnt:4) cost in 5 ms.
numberOfInstances :1
类加载器的个数为一个loadedCountTotal: 2248
共加载了2248个核心类-Xbootclasspath/a:jar包目录/jar包名
进行扩展扩展类加载器和应用程序类加载器都是JDK中提供的、使用Java编写的类加载器
它们的源码都位于sun.misc.Launcher
中,是一个静态内部类,继承自URLClassLoader。通过目录或者指定jar包将字节码文件加载到内存中。
扩展类加载器(Extension Class Loader)是JDK中提供的、使用Java编写的类加载器
默认加载Java安装目录/jre/lib/ext下的类文件
- 类加载器的加载路径可以先使用
classloader -l
查看类的hash值,然后通过classloader –c hash值
查看
|
|
- 类加载器的继承关系可以通过classloader –t 查看
双亲委派机制:当一个类加载器收到类加载请求时,它并不会尝试立即加载这个类,而是把这个请求委派给父类加载器。这样,每个类加载请求都会传递到启动类加载器,只有当父类加载器无法处理这个请求时(例如,类不在它的搜索路径中),子类加载器才会尝试自己去加载。
双亲委派机制口诀:自底向上查找是否加载过,再由顶向下进行加载。
public Class<?> loadClass(String name) throws ClassNotFoundException {
return this.loadClass(name, false); //resolve
}
protected Class<?> loadClass(String name, boolean resolve) throws ClassNotFoundException {
//加锁 保证只有一个线程加载类
synchronized(this.getClassLoadingLock(name)) {
//查询当前类加载器是否加载过需要的类,true 返回类对象 否则,返回null
Class<?> c = this.findLoadedClass(name);
if (c == null) {
long t0 = System.nanoTime();
//向逻辑父类委派加载任务
try {
//直到委派到顶级父类——Extension,进入else分支
if (this.parent != null) {
c = this.parent.loadClass(name, false);
} else {
//调用启动类加载器,进行加载,无法加载返回null
c = findBootstrapClassOrNull(name);
}
} catch (ClassNotFoundException var10) {
}
//所有类加载器加载失败,使用本类加载器进行加载
if (c == null) {
long t1 = System.nanoTime();
c = this.findClass(name);
PerfCounter.getParentDelegationTime().addTime(t1 - t0);
PerfCounter.getFindClassTime().addElapsedTimeFrom(t1);
PerfCounter.getFindClasses().increment();
}
}
if (resolve) {
this.resolveClass(c);
}
return c;
}
}
final Class<?> loadClass(Module module, String name) {
synchronized(this.getClassLoadingLock(name)) {
Class<?> c = this.findLoadedClass(name);
if (c == null) {
c = this.findClass(module.getName(), name);
}
return c != null && c.getModule() == module ? c : null;
}
}
//读取二进制数据
protected Class<?> findClass(String name) throws ClassNotFoundException {
throw new ClassNotFoundException(name);
}
protected Class<?> findClass(String moduleName, String name) {
if (moduleName == null) {
try {
return this.findClass(name);
} catch (ClassNotFoundException var4) {
}
}
return null;
}
//类名校验 将字节码信息加载到虚拟机内存中
protected final Class<?> defineClass(byte[] b, int off, int len) throws ClassFormatError {
return this.defineClass((String)null, b, off, len, (ProtectionDomain)null);
}
protected final Class<?> defineClass(String name, byte[] b, int off, int len) throws ClassFormatError {
return this.defineClass(name, b, off, len, (ProtectionDomain)null);
}
protected final void resolveClass(Class<?> c) {
if (c == null) {
throw new NullPointerException();
}
}
//parent等于null说明父类加载器是启动类加载器,直接调用findBootstrapClassOrNull
//否则调用父类加载器的加载方法
if (parent != null) {
c = parent.loadClass(name, false);
} else {
c = findBootstrapClassOrNull(name);
}
//父类加载器爱莫能助,我来加载!
if (c == null)
c = findClass(name);
public class CustomClassLoader extends ClassLoader {
public CustomClassLoader(ClassLoader parent) {
super(parent);
}
@Override
public Class<?> loadClass(String name) throws ClassNotFoundException {
// 简单的例子, 只处理自定义的类
if (name.startsWith("com.mydomain")) {
return findClass(name);
}
// 其他的类还是采用双亲委派机制加载
return super.loadClass(name);
}
@Override
protected Class<?> findClass(String name) throws ClassNotFoundException {
try {
byte[] bytes = loadClassData(name);
return defineClass(name, bytes, 0, bytes.length);
} catch (IOException e) {
throw new ClassNotFoundException("Cannot load class " + name, e);
}
}
private byte[] loadClassData(String name) throws IOException {
// 根据类名加载类的二进制数据. 假设所有类都在一个固定的目录下
Path path = Paths.get("classes", name.replace('.', '/') + ".class");
return Files.readAllBytes(path);
}
}
DriverManager类位于rt.jar包中,由启动类加载器加载
依赖中的mysql驱动对应的类,由应用程序类加载器来加载。
⚫1. 启动类加载器加载DriverManager。
⚫ 2. 在初始化DriverManager时,通过SPI机制加载jar包中的myql驱动。
⚫ 3. SPI中利用了线程上下文类加载器(应用程序类加载器)去加载类并创建对象。
- 由启动类加载器加载的类,委派应用程序类加载器去加载类的方式,打破了双亲委派机制