JVM类加载机制
待补充
Tomcat类加载器
- 引导类和扩展类加载器作用不变
- 系统类加载器正常情况下加载的是classpath下的类,但是tomcat启动脚本并未使用该变量,而是加载tomcat启动类,如bootstrap.jar , 通常在catalina.bat或者catalina.sh中指定,位于catalina_home/bin下
- common通用类加载器加载tomcat使用以及应用通用的一些类,位于catalina_home/lib下,如servlet-api.jar
- catalinaClassloader用于加载服务器内部可见类,这些类应用程序访问不到
- sharedClassLoader用于加载应用程序共享类,这些服务器不会依赖
- webappClassloader每个应用程序都会有唯一的webappClassloader,用来加载本应用程序/WEB-INF/classes和/WEB-INF/lib下的类 , 不同的webappClassLoader相互隔离
tomcat打破双亲委派
- 从缓存中加载
- 如果没有,先调用ExtClassLoader,扩展类加载器会遵循双亲委派,会调用bootStrapClassloader
a. 获取ExtClassLoader,尝试加载这个类,防止改写java底层类,如java.lang.Object 等
b. 跳过AppClassLoader,达到打破双亲委派的效果,同时也避免底层类加载不到问题 - 如果没有,则从当前类加载器加载,按照WEB-INF/classes , WEB-INF/lib 的顺序加载
- 如果没有,从父类加载器加载,父类加载器默认委派,加载顺序为: Bootstarp/ExtClassLoader/System/common/Shard
源码 : org.apache.catalina.loader.WebappClassLoaderBase#loadClass(java.lang.String, boolean)
public Class<?> loadClass(String name, boolean resolve) throws ClassNotFoundException {
synchronized (JreCompat.isGraalAvailable() ? this : getClassLoadingLock(name)) {
if (log.isDebugEnabled())
log.debug("loadClass(" + name + ", " + resolve + ")");
Class<?> clazz = null;
// Log access to stopped class loader
checkStateForClassLoading(name);
// (0) Check our previously loaded local class cache
//1,检查这个类是不是已经被加载缓存过(tomcat自定义Map缓存)
clazz = findLoadedClass0(name);
if (clazz != null) {
if (log.isDebugEnabled())
log.debug(" Returning class from cache");
if (resolve)
resolveClass(clazz);
return clazz;
}
// (0.1) Check our previously loaded class cache
//如果没被tomcat加载过,从jvm本地缓存中查找
clazz = JreCompat.isGraalAvailable() ? null : findLoadedClass(name);
if (clazz != null) {
if (log.isDebugEnabled())
log.debug(" Returning class from cache");
if (resolve)
resolveClass(clazz);
return clazz;
}
// (0.2) Try loading the class with the system class loader, to prevent
// the webapp from overriding Java SE classes. This implements
// SRV.10.7.2
String resourceName = binaryNameToPath(name, false);
//获取ExtClassLoader,尝试加载这个类,防止改写java底层类,如java.lang.Object 等
//跳过AppClassLoader,达到打破双亲委派的效果,同时也避免底层类加载不到问题
ClassLoader javaseLoader = getJavaseClassLoader();
boolean tryLoadingFromJavaseLoader;
try {
// Use getResource as it won't trigger an expensive
// ClassNotFoundException if the resource is not available from
// the Java SE class loader. However (see
// https://bz.apache.org/bugzilla/show_bug.cgi?id=58125 for
// details) when running under a security manager in rare cases
// this call may trigger a ClassCircularityError.
// See https://bz.apache.org/bugzilla/show_bug.cgi?id=61424 for
// details of how this may trigger a StackOverflowError
// Given these reported errors, catch Throwable to ensure any
// other edge cases are also caught
URL url;
//安全管理器
if (securityManager != null) {
PrivilegedAction<URL> dp = new PrivilegedJavaseGetResource(resourceName);
url = AccessController.doPrivileged(dp);
} else {
url = javaseLoader.getResource(resourceName);
}
tryLoadingFromJavaseLoader = (url != null);
} catch (Throwable t) {
// Swallow all exceptions apart from those that must be re-thrown
ExceptionUtils.handleThrowable(t);
// The getResource() trick won't work for this class. We have to
// try loading it directly and accept that we might get a
// ClassNotFoundException.
tryLoadingFromJavaseLoader = true;
}
if (tryLoadingFromJavaseLoader) {
try {
clazz = javaseLoader.loadClass(name);
if (clazz != null) {
if (resolve)
resolveClass(clazz);
return clazz;
}
} catch (ClassNotFoundException e) {
// Ignore
}
}
// (0.5) Permission to access this class when using a SecurityManager
if (securityManager != null) {
int i = name.lastIndexOf('.');
if (i >= 0) {
try {
securityManager.checkPackageAccess(name.substring(0, i));
} catch (SecurityException se) {
String error = sm.getString("webappClassLoader.restrictedPackage", name);
log.info(error, se);
throw new ClassNotFoundException(error, se);
}
}
}
//是否配置了委派
boolean delegateLoad = delegate || filter(name, true);
// (1) Delegate to our parent if requested
//委派给父类加载器先加载,比如tomcat中CommonClassLoader等,加载tomcat源码相关包路径的class
if (delegateLoad) {
if (log.isDebugEnabled())
log.debug(" Delegating to parent classloader1 " + parent);
try {
//使用当前加载器的父类加载器去加载这个类(双亲委派)
clazz = Class.forName(name, false, parent);
if (clazz != null) {
if (log.isDebugEnabled())
log.debug(" Loading class from parent");
if (resolve)
resolveClass(clazz);
return clazz;
}
} catch (ClassNotFoundException e) {
// Ignore
}
}
// (2) Search local repositories
if (log.isDebugEnabled())
log.debug(" Searching local repositories");
try {
//如果没有加载到,再用自己的加载器加载
clazz = findClass(name);
if (clazz != null) {
if (log.isDebugEnabled())
log.debug(" Loading class from local repository");
if (resolve)
resolveClass(clazz);
return clazz;
}
} catch (ClassNotFoundException e) {
// Ignore
}
// (3) Delegate to parent unconditionally
//没有开启委派,自己classLoader加载不到
if (!delegateLoad) {
if (log.isDebugEnabled())
log.debug(" Delegating to parent classloader at end: " + parent);
try {
//再由父类classLoader加载(双亲委派)
clazz = Class.forName(name, false, parent);
if (clazz != null) {
if (log.isDebugEnabled())
log.debug(" Loading class from parent");
if (resolve)
resolveClass(clazz);
return clazz;
}
} catch (ClassNotFoundException e) {
// Ignore
}
}
}
throw new ClassNotFoundException(name);
}