【心得分享】-- 类的加载机制深度解析

    技术2025-11-04  29

    1. 类的加载机制深度解析

    1.1 类的加载运行过程

    如上图所示,Java中的类的加载靠的是类加载器实现的,其中通过loadClass进行类加载的时候会经历:加载》》验证》》准备》》解析》》初始化几个步骤。

    加载:查找磁盘中的类的字节码文件并通过io操作读入,只有类被真正使用的时候才会被加载验证:验证字节码是否符合规范准备:为静态变量分配内存空间,并且赋上默认的初始值。int类型赋值0、boolean类型赋值false、对象类型赋值null解析:将符号引用(静态方法)转换为直接引用初始化:为静态变量赋上真正的值

    注意:主类在运行时如果使用到其他类,这些类会被逐步加载,jar包和war包中的类并不是一次性都加载出来的,而是真正使用到这个类的时候才会进行加载

    package com.muzili.jvm; public class TestDynamicLoad { public static String initDate="test"; static { System.out.println("运行TestDynamicLoad类的静态方法....."); } public TestDynamicLoad(){ System.out.println("运行TestDynamicLoad类的构造方法....."); } public static class InnerClassA{ static { System.out.println("运行InnerClassA类的静态方法....."); } public InnerClassA(){ System.out.println("运行InnerClassA类的构造方法....."); } } public static void main(String[] args) { TestDynamicLoad testDynamicLoad=new TestDynamicLoad(); InnerClassA innerClassA=new InnerClassA(); } } 结果输出: 运行TestDynamicLoad类的静态方法..... 运行TestDynamicLoad类的构造方法..... 运行InnerClassA类的静态方法..... 运行InnerClassA类的构造方法..... Process finished with exit code 0

    1.2 类加载器和双亲委派机制

    1.2.1 类加载器

    Java中的类加载器主要有三种:BootstrapClassLoader(引导类加载器)、ExtClassLoader(扩展类加载器)、AppClassLoader(应用程序类加载器)。

    BootstrapClassLoader:主要用于加载Java程序运行时必不可少的核心类,如jre/lib/rt.jar、jre/lib/charsets.jar中的类。ExtClassLoader:用于加载jre/lib/ext下的所有jar包中的类AppClassLoader:用于加载自己编写的类 package com.muzili.jvm; import com.sun.crypto.provider.DESKeyFactory; import sun.misc.Launcher; import java.net.URL; public class TestJDKClassLoader { public static void main(String[] args) { /** * 输出以下类的ClassLoader */ System.out.println(String.class.getClassLoader()); System.out.println(DESKeyFactory.class.getClassLoader()); System.out.println(TestJDKClassLoader.class.getClassLoader()); System.out.println(); /** * 获取ClassLoader */ //因为BootStrapClassLoader是通过C++实现,并实例化的故Java程序无法获取 ClassLoader appClassLoader=TestJDKClassLoader.class.getClassLoader(); ClassLoader extClassLoader=appClassLoader.getParent(); ClassLoader bootstrapClassLoader=extClassLoader.getParent(); System.out.println("the BootStrapClassLoader:"+bootstrapClassLoader); System.out.println("the ExtClassLoader:"+extClassLoader); System.out.println("the AppClassLoader:"+appClassLoader); System.out.println(); URL[] bootStrapUrls = Launcher.getBootstrapClassPath().getURLs(); System.out.println("BootStrapLoader加载:"); for (URL url : bootStrapUrls) { System.out.println(url.getPath()); } System.out.println(); System.out.println("ExtClassLoader加载:"); System.out.println(System.getProperty("java.ext.dirs")); System.out.println(); System.out.println("AppClassLoader加载:"); System.out.println(System.getProperty("java.class.path")); } } 结果输出: null sun.misc.Launcher$ExtClassLoader@4b67cf4d sun.misc.Launcher$AppClassLoader@18b4aac2 the BootStrapClassLoader:null the ExtClassLoader:sun.misc.Launcher$ExtClassLoader@4b67cf4d the AppClassLoader:sun.misc.Launcher$AppClassLoader@18b4aac2 BootStrapLoader加载: /E:/Program%20Files/Java/jdk1.8.0_191/jre/lib/resources.jar /E:/Program%20Files/Java/jdk1.8.0_191/jre/lib/rt.jar /E:/Program%20Files/Java/jdk1.8.0_191/jre/lib/sunrsasign.jar /E:/Program%20Files/Java/jdk1.8.0_191/jre/lib/jsse.jar /E:/Program%20Files/Java/jdk1.8.0_191/jre/lib/jce.jar /E:/Program%20Files/Java/jdk1.8.0_191/jre/lib/charsets.jar /E:/Program%20Files/Java/jdk1.8.0_191/jre/lib/jfr.jar /E:/Program%20Files/Java/jdk1.8.0_191/jre/classes ExtClassLoader加载: E:\Program Files\Java\jdk1.8.0_191\jre\lib\ext;C:\WINDOWS\Sun\Java\lib\ext AppClassLoader加载: E:\Program Files\Java\jdk1.8.0_191\jre\lib\charsets.jar;E:\Program Files\Java\jdk1.8.0_191\jre\lib\deploy.jar;E:\Program Files\Java\jdk1.8.0_191\jre\lib\ext\access-bridge-64.jar;E:\Program Files\Java\jdk1.8.0_191\jre\lib\ext\cldrdata.jar;E:\Program Files\Java\jdk1.8.0_191\jre\lib\ext\dnsns.jar;E:\Program Files\Java\jdk1.8.0_191\jre\lib\ext\jaccess.jar;E:\Program Files\Java\jdk1.8.0_191\jre\lib\ext\jfxrt.jar;E:\Program Files\Java\jdk1.8.0_191\jre\lib\ext\localedata.jar;E:\Program Files\Java\jdk1.8.0_191\jre\lib\ext\nashorn.jar;E:\Program Files\Java\jdk1.8.0_191\jre\lib\ext\sunec.jar;E:\Program Files\Java\jdk1.8.0_191\jre\lib\ext\sunjce_provider.jar;E:\Program Files\Java\jdk1.8.0_191\jre\lib\ext\sunmscapi.jar;E:\Program Files\Java\jdk1.8.0_191\jre\lib\ext\sunpkcs11.jar;E:\Program Files\Java\jdk1.8.0_191\jre\lib\ext\zipfs.jar;E:\Program Files\Java\jdk1.8.0_191\jre\lib\javaws.jar;E:\Program Files\Java\jdk1.8.0_191\jre\lib\jce.jar;E:\Program Files\Java\jdk1.8.0_191\jre\lib\jfr.jar;E:\Program Files\Java\jdk1.8.0_191\jre\lib\jfxswt.jar;E:\Program Files\Java\jdk1.8.0_191\jre\lib\jsse.jar;E:\Program Files\Java\jdk1.8.0_191\jre\lib\management-agent.jar;E:\Program Files\Java\jdk1.8.0_191\jre\lib\plugin.jar;E:\Program Files\Java\jdk1.8.0_191\jre\lib\resources.jar;E:\Program Files\Java\jdk1.8.0_191\jre\lib\rt.jar;D:\workspace\tulingxueyuan-demo\out\production\jvm-demo-01;E:\Program Files\JetBrains\IntelliJ IDEA 2019.1.1\lib\idea_rt.jar Process finished with exit code 0

    类加载过程解析:

    通过jvm创建BootstrapClassLoader实例,调用sun.misc.Launcher#getLauncher()方法获取启动器对象【这里使用到了设计模式中的单例模式】Launcher类的构造方法中,创建了两个类加载器:sun.misc.Launcher.ExtClassLoader#getExtClassLoader[扩展类加载器]、sun.misc.Launcher.AppClassLoader#getAppClassLoader[应用程序类加载器]JVM默认会使用Launcher的getClassLoader()方法返回的类加载器AppClassLoader用于加载我们的类 // // Source code recreated from a .class file by IntelliJ IDEA // (powered by Fernflower decompiler) // package sun.misc; import java.io.File; import java.io.IOException; import java.net.MalformedURLException; import java.net.URI; import java.net.URISyntaxException; import java.net.URL; import java.net.URLClassLoader; import java.net.URLStreamHandler; import java.net.URLStreamHandlerFactory; import java.nio.file.Paths; import java.security.AccessControlContext; import java.security.AccessController; import java.security.CodeSource; import java.security.PermissionCollection; import java.security.PrivilegedAction; import java.security.PrivilegedActionException; import java.security.PrivilegedExceptionAction; import java.security.ProtectionDomain; import java.security.cert.Certificate; import java.util.HashSet; import java.util.StringTokenizer; import java.util.Vector; import sun.net.www.ParseUtil; public class Launcher { private static URLStreamHandlerFactory factory = new Launcher.Factory(); private static Launcher launcher = new Launcher(); private static String bootClassPath = System.getProperty("sun.boot.class.path"); private ClassLoader loader; private static URLStreamHandler fileHandler; public static Launcher getLauncher() { return launcher; } public Launcher() { Launcher.ExtClassLoader var1; try { var1 = Launcher.ExtClassLoader.getExtClassLoader(); } catch (IOException var10) { throw new InternalError("Could not create extension class loader", var10); } try { this.loader = Launcher.AppClassLoader.getAppClassLoader(var1); } 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); } } public ClassLoader getClassLoader() { return this.loader; } //省略一些其他代码 }

    1.2.2 双亲委派机制

    双亲委派机制,就是在加载类的时候,级别较低的类加载器首先会委托它的父级类加载器进行加载,如果父级类加载器加载不到才会交给当前类加载器进行加载。

    双亲委派机制有什么意义:

    沙箱安全:它可以保证java中的核心类不会被用户定义的类给覆盖,从而保证了系统的安全防止类的重复加载:每个类加载器,都实现了缓存机制,当类第一次被加载之后就会被缓存起来,当再次加载的时候,就不用再每次都按照双亲委派机制来加载类,这样可以提升类的加载效率

    1.3 自定义类加载器

    为什么需要自定义类加载器?

    如果在实际开发中,可能有时我们想要加载一些外部目录下的类,这个时候jvm提供的类加载器就无法满足我们的需求了,这个时候我们需要自定义类加载器,用以加载外部目录中的类。

    1.3.1 定义一个简单的类加载器

    package com.muzili.jvm; import java.io.FileInputStream; import java.io.IOException; public class MyClassLoaderTest { public static class MyClassLoader extends ClassLoader{ private String clasasPath; public MyClassLoader(String clasasPath) { this.clasasPath = clasasPath; } private byte[] loadByte(String name) throws IOException { name = name.replace(".", "/"); FileInputStream fis=new FileInputStream(clasasPath+"/"+name+".class"); int len=fis.available(); byte[] bytes=new byte[len]; fis.read(bytes); fis.close(); return bytes; } @Override protected Class<?> findClass(String name) throws ClassNotFoundException { try { byte[] bytes = this.loadByte(name); return defineClass(name,bytes,0,bytes.length); } catch (IOException e) { e.printStackTrace(); } return null; } } public static void main(String[] args) { //初始化自定义的类加载器 MyClassLoader myClassLoader=new MyClassLoader("D:\\test"); try { Class<?> clazz = myClassLoader.loadClass("com.muzili.jvm.User1"); System.out.println(clazz.getClassLoader()); } catch (ClassNotFoundException e) { e.printStackTrace(); } } } 运行结果: com.muzili.jvm.MyClassLoaderTest$MyClassLoader@4554617c Process finished with exit code 0

    1.3.2 打破类加载器的双亲委派机制

    package com.muzili.jvm; import java.io.FileInputStream; import java.io.IOException; public class MyClassLoaderTest2 { public static class MyClassLoader extends ClassLoader{ private String clasasPath; public MyClassLoader(String clasasPath) { this.clasasPath = clasasPath; } private byte[] loadByte(String name) throws IOException { name = name.replace(".", "/"); FileInputStream fis=new FileInputStream(clasasPath+"/"+name+".class"); int len=fis.available(); byte[] bytes=new byte[len]; fis.read(bytes); fis.close(); return bytes; } @Override protected Class<?> findClass(String name) throws ClassNotFoundException { try { byte[] bytes = this.loadByte(name); return defineClass(name,bytes,0,bytes.length); } catch (IOException e) { e.printStackTrace(); } return null; } /** * 打破双亲委派机制 * @param name * @return * @throws ClassNotFoundException */ @Override public 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(); //直接将双亲委派机制取消不做任何处理会导致java运行时非常重要的类无法加载 if (!name.startsWith("com.muzili.jvm")){ c=this.getParent().loadClass(name); } if (c == null) { // If still not found, then invoke findClass in order // to find the class. long t1 = System.nanoTime(); c = findClass(name); // 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; } } } public static void main(String[] args) { //初始化自定义的类加载器 MyClassLoader myClassLoader=new MyClassLoader("D:\\test"); try { Class<?> clazz = myClassLoader.loadClass("com.muzili.jvm.User1"); System.out.println(clazz); } catch (ClassNotFoundException e) { e.printStackTrace(); } } } 结果输出: class com.muzili.jvm.User1
    Processed: 0.051, SQL: 9