JVM 类加载机制 学习记录 1
java com.yyds.jvm.Math.class 运行Math 类的过程如下:
启动Java.exe,调用底层 C++ 库 来创建 Java 虚拟机(C++实现),然后创建一个引导类加载器(C++实现),再调用Java代码创建JVM 启动器实例:sun.misc.Launcher 的实例, 此实例初始化的过程中,会创建其他的类加载器,包括扩展类加载器(ExtClassLoader) 和 应用类加载器(AppClassLoader), 使用类加载器加载要运行的类Math, 加载完成后执行 Math.main() 方法,运行结束,JVM销毁。
其中Java 部分的步骤如下:
1、创建Launcher(单例)实例,创建其他加载器
源码如下:
public Launcher() { Launcher.ExtClassLoader var1; try { var1 = Launcher.ExtClassLoader.getExtClassLoader(); //创建ExtClassLoader 对象(单例) } catch (IOException var10) { throw new InternalError("Could not create extension class loader", var10); } try { this.loader = Launcher.AppClassLoader.getAppClassLoader(var1); //创建AppClassLoader 对象(单例),并制定 var1(ExtClassLoader) 为parent属性 } catch (IOException var9) { throw new InternalError("Could not create application class loader", var9); } Thread.currentThread().setContextClassLoader(this.loader); String var2 = System.getProperty("java.security.manager"); if (var2 != null) { SecurityManager var3 = null; if (!"".equals(var2) && !"default".equals(var2)) { try { var3 = (SecurityManager)this.loader.loadClass(var2).newInstance(); } catch (IllegalAccessException var5) { } catch (InstantiationException var6) { } catch (ClassNotFoundException var7) { } catch (ClassCastException var8) { } } else { var3 = new SecurityManager(); } if (var3 == null) { throw new InternalError("Could not create SecurityManager: " + var2); } System.setSecurityManager(var3); } }

static class ExtClassLoader extends URLClassLoader { private static volatile Launcher.ExtClassLoader instance; public static Launcher.ExtClassLoader getExtClassLoader() throws IOException { if (instance == null) { Class var0 = Launcher.ExtClassLoader.class; synchronized(Launcher.ExtClassLoader.class) { if (instance == null) { instance = createExtClassLoader(); } } } return instance; } private static Launcher.ExtClassLoader createExtClassLoader() throws IOException { try { return (Launcher.ExtClassLoader)AccessController.doPrivileged(new PrivilegedExceptionAction<Launcher.ExtClassLoader>() { public Launcher.ExtClassLoader run() throws IOException { File[] var1 = Launcher.ExtClassLoader.getExtDirs(); int var2 = var1.length; for(int var3 = 0; var3 < var2; ++var3) { MetaIndex.registerDirectory(var1[var3]); } return new Launcher.ExtClassLoader(var1); } }); } catch (PrivilegedActionException var1) { throw (IOException)var1.getException(); } } void addExtURL(URL var1) { super.addURL(var1); } public ExtClassLoader(File[] var1) throws IOException { super(getExtURLs(var1), (ClassLoader)null, Launcher.factory); // 第二个参数为null,是因为引导类加载器是C++实现的,此处没有,所以ExtClassLoader.getParent == null SharedSecrets.getJavaNetAccess().getURLClassPath(this).initLookupCache(this); } ... }

static class AppClassLoader extends URLClassLoader { //继承URLClassLoader final URLClassPath ucp = SharedSecrets.getJavaNetAccess().getURLClassPath(this); public static ClassLoader getAppClassLoader(final ClassLoader var0) throws IOException { final String var1 = System.getProperty("java.class.path"); //扫描 java/class/path 路径下的字节码文件 final File[] var2 = var1 == null ? new File[0] : Launcher.getClassPath(var1); return (ClassLoader)AccessController.doPrivileged(new PrivilegedAction<Launcher.AppClassLoader>() { public Launcher.AppClassLoader run() { URL[] var1x = var1 == null ? new URL[0] : Launcher.pathToURLs(var2); return new Launcher.AppClassLoader(var1x, var0); } }); } AppClassLoader(URL[] var1, ClassLoader var2) { super(var1, var2, Launcher.factory); this.ucp.initLookupCache(this); } ... }
2、类加载器 加载 Math 类:
从上图可看到把当前线程类加载器设置为了AppClassLoader, 所以此处会先调用 AppClassLoader 类的 loadClass 方法加载 Math 类,源码如下, 经过判断后,如果未加载
public Class<?> loadClass(String var1, boolean var2) throws ClassNotFoundException { int var3 = var1.lastIndexOf(46); if (var3 != -1) { SecurityManager var4 = System.getSecurityManager(); if (var4 != null) { var4.checkPackageAccess(var1.substring(0, var3)); } } if (this.ucp.knownToNotExist(var1)) { Class var5 = this.findLoadedClass(var1); //判断类是否已加载 if (var5 != null) { if (var2) { this.resolveClass(var5); } return var5; } else { throw new ClassNotFoundException(var1); } } else { return super.loadClass(var1, var2); //正常情况调用父类URLClassLoader 的 loadClass } }
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 { if (parent != null) { c = parent.loadClass(name, false); //调用父加载器的loadclass方法,如果父加载器不为null,则一直向上调用 双亲委派机制实现 } else { c = findBootstrapClassOrNull(name); //因为扩展类加载器的parent是null,代表引导类加载器加载类 } } catch (ClassNotFoundException e) { // ClassNotFoundException thrown if class not found // from the non-null parent class loader } if (c == null) { // If still not found, then invoke findClass in order // to find the class. long t1 = System.nanoTime(); c = findClass(name); // 如果顶层父加载器(引导类加载器)都没有加载到类,则递归返回后依次调用findclass 加载 // 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; } }
// ClassLoader.java 没有具体实现,需要看到对应的子类URLClassLoader里的实现 protected Class<?> findClass(String name) throws ClassNotFoundException { throw new ClassNotFoundException(name); } // URLClassLoader.class protected Class<?> findClass(final String name) throws ClassNotFoundException { final Class<?> result; try { result = 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 { return null; } } }, acc); } catch (java.security.PrivilegedActionException pae) { throw (ClassNotFoundException) pae.getException(); } if (result == null) { throw new ClassNotFoundException(name); } return result; }
3、 调用入口 main 方法
双亲委派机制:
其中双亲委派机制即是: 当前类加载器去加载类的时候,如果发现类没有被加载,则优先去使用父加载器去加载,依次直到引导类加载器,如果引导类加载器没有加载到类,则依次向后再去加载,直到加载到类。
打破双亲委派机制: 例如 tomcat 打破双亲委派机制。
为什么要打破双亲委派机制: 当一个类的不同版本使用同一个限定类名都时候,如果其中一个项目加载了1.x版本的类, 则另外的项目无法再加载 2.x 版本的类,会导致问题。
使用自定义类加载器打破双亲委派机制, 指定自定义类加载器扫描的路径,重写 loadclass 方法,当调用非Java 本身类的时候,加载Class 去掉父加载优先的规则,直接使用当前自定义类加载器去加载。