本文共 10512 字,大约阅读时间需要 35 分钟。
package study.wyy.jvm.classLoader;import study.wyy.jvm.model.User;public class Math { public static User user = new User(); public static int initData = 666; // 一个方法对应一个栈帧内存区域 public int compute() { int a = 1; int b = 2; int c = (a + b) * 10; return c; } public static void main(String[] args) { Math math = new Math(); math.compute(); }}
上面这个代码的大致过程就是如下:
静态链接过程
(类加载期间完成)扩展: 动态链接是在程序运行期间完成的将符号引用替换为直接引用。
主类在运行过程中如果使用到其它类,会逐步加载这些类。 jar包或war包里的类不是一次性全部加载的,是使用到时才加载。
简单演示一下下:
package study.wyy.jvm.classLoader;public class TestDynamicLoad { static { System.out.println("*************load TestDynamicLoad************"); } public static void main(String[] args) { new A(); System.out.println("*************load test************"); // 这里没去new B,只是声明了而已 B b = null; }}class A { static { System.out.println("*************load A ************"); } public A() { System.out.println("*************initial A ************"); }}class B { static { System.out.println("*************load B ************"); } public B() { System.out.println("*************initial B ************"); }}
分析代码特点:
输出结果
*************load TestDynamicLoad*************************load A *************************initial A *************************load test************
总结
上面的类加载过程主要是通过类加载器来实现的,Java里有如下几种类加载器
简单演示:
public class TestJDKClassLoader { public static void main(String[] args) { System.out.println(String.class.getClassLoader()); System.out.println(DESKeyFactory.class.getClassLoader()); System.out.println(TestJDKClassLoader.class.getClassLoader()); System.out.println(); }}
输出结果
nullsun.misc.Launcher$ExtClassLoader@7f31245asun.misc.Launcher$AppClassLoader@18b4aac2
为什么String的ClassLoader是null
String 是java的核心基础类,由引导类加载器加载,而引导类加载器是C++实现的,是C++的对象,在Java中是拿不到的。
开头的图中已经说了:C++会调用java代码(getLauncher方法)创建jvm启动器sun.misc.Launcher#Launcher
,再有这个类去加载其他的类加载器(除了引导类加载器)
部分源码
public class Launcher { private static URLStreamHandlerFactory factory = new Factory(); // 可见在这个加载的时候就会new这个类的对象, private static Launcher launcher = new Launcher(); private static String bootClassPath = System.getProperty("sun.boot.class.path"); public static Launcher getLauncher() { return launcher; } // 构造方法的时候会给其赋值为APPClassLoader private ClassLoader loader; 。。。
在看一下其构造方法
public Launcher() { // Create the extension class loader ClassLoader extcl; try { // 获取ExtClassLoader(扩展类加载器) extcl = ExtClassLoader.getExtClassLoader(); } catch (IOException e) { throw new InternalError( "Could not create extension class loader", e); } // Now create the class loader to use to launch the application try { // 其赋值为APPClassLoader loader = AppClassLoader.getAppClassLoader(extcl); } catch (IOException e) { throw new InternalError( "Could not create application class loader", e); } // Also set the context class loader for the primordial thread. Thread.currentThread().setContextClassLoader(loader); // Finally, install a security manager if requested String s = System.getProperty("java.security.manager"); if (s != null) { // init FileSystem machinery before SecurityManager installation sun.nio.fs.DefaultFileSystemProvider.create(); 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); } } }
ExtClassLoader.getExtClassLoader();
/** * create an ExtClassLoader. The ExtClassLoader is created * within a context that limits which files it can read */ public static ExtClassLoader getExtClassLoader() throws IOException { if (instance == null) { synchronized(ExtClassLoader.class) { if (instance == null) { instance = createExtClassLoader(); } } } return instance; }
createExtClassLoader()
private static ExtClassLoader createExtClassLoader() throws IOException { try { // Prior implementations of this doPrivileged() block supplied // aa synthesized ACC via a call to the private method // ExtClassLoader.getContext(). return AccessController.doPrivileged( new PrivilegedExceptionAction() { public ExtClassLoader run() throws IOException { // getExtDirs:获取 String s = System.getProperty("java.ext.dirs"); // 这个路径下的class文件 final File[] dirs = getExtDirs(); int len = dirs.length; for (int i = 0; i < len; i++) { MetaIndex.registerDirectory(dirs[i]); } // 主要是这里构建了ExtClassLoader,上面是一些安全检查 return new ExtClassLoader(dirs); } }); } catch (java.security.PrivilegedActionException e) { throw (IOException) e.getException(); } }
new ExtClassLoader(dirs)
/* * Creates a new ExtClassLoader for the specified directories. */ public ExtClassLoader(File[] dirs) throws IOException { // 调用父类UrLClassLoader的构造,加载我们磁盘上的文件, // 注意这里的第二个参数是null super(getExtURLs(dirs), null, factory); SharedSecrets.getJavaNetAccess(). getURLClassPath(this).initLookupCache(this); }
loader = AppClassLoader.getAppClassLoader(extcl);
注意这里将刚刚的extClassLoader传了进来
public static ClassLoader getAppClassLoader(final ClassLoader extcl) throws IOException { // 获取环境变量,可以打印看一下,就是其一堆java的路径 final String s = System.getProperty("java.class.path"); final File[] path = (s == null) ? new File[0] : getClassPath(s); // 安全检查 return AccessController.doPrivileged( new PrivilegedAction() { public AppClassLoader run() { URL[] urls = (s == null) ? new URL[0] : pathToURLs(path); return new AppClassLoader(urls, extcl); } }); }
new AppClassLoader(urls, extcl);
// ClassLoader parent: extClassLoaderAppClassLoader(URL[] urls, ClassLoader parent) { // // 调用父类UrLClassLoader的构造,加载我们磁盘上的文件, // 这里构造AppClassLoader第二个参数为extClassLoader,而上面构造extClassLoader传递的是null super(urls, parent, factory); ucp = SharedSecrets.getJavaNetAccess().getURLClassPath(this); ucp.initLookupCache(this);}
起初传递来的extClassLoader,干嘛了呢
**最终会在赋值给parent,也就是说AppClassLoader的parent属性是extClassLoader, extClassLoader的parent属性是null,这里的null可以理解为引导类加载器(前面提到了引导类加载器是c++实现,在JAVA层面是拿不到的,所以上面获取string类的累加载器为null) **
// java.lang.ClassLoaderprivate ClassLoader(Void unused, ClassLoader parent) { this.parent = parent; if (ParallelLoaders.isRegistered(this.getClass())) { parallelLockMap = new ConcurrentHashMap<>(); package2certs = new ConcurrentHashMap<>(); assertionLock = new Object(); } else { // no finer-grained lock; lock on the classloader instance parallelLockMap = null; package2certs = new Hashtable<>(); assertionLock = this; } }
回头看一下extClassLoader的parent属性是什么呢?
public ExtClassLoader(File[] dirs) throws IOException { // 传了一个null,这里就可以理解为之前的引导类加载器,之前打印的是她就是个null super(getExtURLs(dirs), null, factory); SharedSecrets.getJavaNetAccess(). getURLClassPath(this).initLookupCache(this); }
类加载器初始化过程: 参见类运行加载全过程图可知其中会创建JVM启动器实例sun.misc.Launcher。 sun.misc.Launcher初始化使用了单例模式设计,保证一个JVM虚拟机内只有一个 sun.misc.Launcher实例。 在Launcher构造方法内部,其创建了两个类加载器,分别是 sun.misc.Launcher.ExtClassLoader(扩展类加载器)和sun.misc.Launcher.AppClassLoader(应 用类加载器)。 JVM默认使用Launcher的getClassLoader()方法返回的类加载器AppClassLoader的实例加载我们 的应用程序。
简单验证一下
public static void main(String[] args) { ClassLoader appClassLoader = ClassLoader.getSystemClassLoader(); ClassLoader extClassLoader = appClassLoader.getParent(); ClassLoader bootstrapLoader = extClassLoader.getParent(); System.out.println("system classLoader: " + appClassLoader); System.out.println("appClassLoader parent: " + extClassLoader); System.out.println("extClassLoader parent: " + bootstrapLoader); }
输出
system classLoader: sun.misc.Launcher$AppClassLoader@18b4aac2appClassLoader parent: sun.misc.Launcher$ExtClassLoader@7f31245aextClassLoader parent: null
转载地址:http://wbrv.baihongyu.com/