VB.net 2010 视频教程 VB.net 2010 视频教程 python基础视频教程
SQL Server 2008 视频教程 c#入门经典教程 Visual Basic从门到精通视频教程
当前位置:
首页 > 编程开发 > Java教程 >
  • JVM 类加载

三层类加载器及父子关系建立

1.1 三层类加载器

BootStrapClassLoader:

查看加载路劲方法:

复制代码
URL[] urLs = sun.misc.Launcher.getBootstrapClassPath().getURLs();

for (URL urL : urLs) {

    System.out.println(url.toExternalForm());    

}
复制代码

 

运行结果:

file:/C:/Java/jdk1.8.0_101/jre/lib/resources.jar

file:/C:/Java/jdk1.8.0_101/jre/lib/rt.jar

file:/C:/Java/jdk1.8.0_101/jre/lib/sunrsasign.jar

file:/C:/Java/jdk1.8.0_101/jre/lib/jsse.jar

file:/C:/Java/jdk1.8.0_101/jre/lib/jce.jar

file:/C:/Java/jdk1.8.0_101/jre/lib/charsets.jar

file:/C:/Java/jdk1.8.0_101/jre/lib/jfr.jar

file:/C:/Java/jdk1.8.0_101/jre/classes

启动类加载器是由C++实现的加载器,没有对应的.Java类,主要加载JDK核心类库。如:rt.jar 也可以通过-Xbootclasspath指定 对应openJdk源码 参考:

 

复制代码
 1 int JNICALL
 2 JavaMain(void * _args)
 3 {
 4     ……
 5     mainClass = LoadMainClass(env, mode, what);
 6     ……
 7 }
 8 
 9 static jclass
10 LoadMainClass(JNIEnv *env, int mode, char *name)
11 {
12     jmethodID mid;
13     jstring str;
14     jobject result;
15     jlong start, end;
16     jclass cls = GetLauncherHelperClass(env);
17     NULL_CHECK0(cls);
18     if (JLI_IsTraceLauncher()) {
19         start = CounterGet();
20     }
21     //核心方法调用 checkAndLoadMain
22     NULL_CHECK0(mid = (*env)->GetStaticMethodID(env, cls,
23                 "checkAndLoadMain",
24                 "(ZILjava/lang/String;)Ljava/lang/Class;"));
25 
26     str = NewPlatformString(env, name);
27     CHECK_JNI_RETURN_0(
28         result = (*env)->CallStaticObjectMethod(
29             env, cls, mid, USE_STDERR, mode, str));
30 
31     if (JLI_IsTraceLauncher()) {
32         end   = CounterGet();
33         printf("%ld micro seconds to load main class\n",
34                (long)(jint)Counter2Micros(end-start));
35         printf("----%s----\n", JLDEBUG_ENV_ENTRY);
36     }
37 
38     return (jclass)result;
39 }
40 
41 jclass
42 GetLauncherHelperClass(JNIEnv *env)
43 {
44     if (helperClass == NULL) {
45         NULL_CHECK0(helperClass = FindBootStrapClass(env,
46                 "sun/launcher/LauncherHelper"));
47     }
48     return helperClass;
49 }
50 
51 jclass
52 FindBootStrapClass(JNIEnv *env, const char* classname)
53 {
54    if (findBootClass == NULL) {
55        findBootClass = (FindClassFromBootLoader_t *)dlsym(RTLD_DEFAULT,
56           "JVM_FindClassFromBootLoader");
57        if (findBootClass == NULL) {
58            JLI_ReportErrorMessage(DLL_ERROR4,
59                "JVM_FindClassFromBootLoader");
60            return NULL;
61        }
62    }
63    return findBootClass(env, classname);
64 }
65 
66 JVM_ENTRY(jclass, JVM_FindClassFromBootLoader(JNIEnv* env,
67                                               const char* name))
68   JVMWrapper2("JVM_FindClassFromBootLoader %s", name);
69 
70   // Java libraries should ensure that name is never null...
71   if (name == NULL || (int)strlen(name) > Symbol::max_length()) {
72     // It's impossible to create this class;  the name cannot fit
73     // into the constant pool.
74     return NULL;
75   }
76 
77   TempNewSymbol h_name = SymbolTable::new_symbol(name, CHECK_NULL);
78   Klass* k = SystemDictionary::resolve_or_null(h_name, CHECK_NULL);
79   if (k == NULL) {
80     return NULL;
81   }
82 
83   if (TraceClassResolution) {
84     trace_class_resolution(k);
85   }
86   return (jclass) JNIHandles::make_local(env, k->java_mirror());
87 JVM_END
复制代码

 

这套逻辑做的事情就是通过启动类加载器加载类sun.launcher.LauncherHelper,执行该类的方法checkAndLoadMain,加载main函数所在的类,启动扩展类加载器、应用类加载器也是在这个时候完成的

 

ExtClassLoader:

查看加载路劲方法:

复制代码
 1  public static void main(String[] args) {
 2         ClassLoader classLoader = ClassLoader.getSystemClassLoader().getParent();
 3 
 4         URLClassLoader urlClassLoader = (URLClassLoader) classLoader;
 5 
 6         URL[] urls = urlClassLoader.getURLs();
 7         for (URL url : urls) {
 8             System.out.println(url);
 9         }
10     }
复制代码

 

 

拓展类加载器:主要负责加载Java的扩展类库,默认加载JAVA_HOME/jre/lib/ext/目录下的所有jar包或者由java.ext.dirs系统属性指定的jar包

AppClassLoader:

查看加载路劲方法:

复制代码
 1  public static void main(String[] args) {
 2         String[] urls = System.getProperty("java.class.path").split(":");
 3 
 4         for (String url : urls) {
 5             System.out.println(url);
 6         }
 7 
 8         System.out.println("================================");
 9 
10         URLClassLoader classLoader = (URLClassLoader) ClassLoader.getSystemClassLoader();
11 
12         URL[] urls1 = classLoader.getURLs();
13         for (URL url : urls1) {
14             System.out.println(url);
15         }
16     }
复制代码

 

 

应用类加载器: 又称为系统类加载器,负责在JVM启动时,加载来自在命令java中的classpath或者java.class.path系统属性或者CLASSPATH操作系统属性所指定的JAR类包和类路径.

 

自定义类加载器:

复制代码
 1 public class Classloader_1 extends ClassLoader {
 2 
 3     public static void main(String[] args) {
 4         Classloader_1 classloader = new Classloader_1();
 5 
 6         try {
 7             Class<?> clazz = classloader.loadClass(Classloader_1_A.class.getName());
 8 
 9             System.out.println(clazz);
10             System.out.println(clazz.getClassLoader());
11         } catch (ClassNotFoundException e) {
12             e.printStackTrace();
13         }
14     }
15 
16 }
复制代码

 

 

运行结果:

class com.luban.ziya.classloader.Classloader_1_A

jdk.internal.loader.ClassLoaders$AppClassLoader@2f0e140b

为什么还是AppClassLoader呢 ?

因为双亲委派模型,自定义类加载器的父类能够加载到这个类

可以重写 loadClass方法 实现拒绝 AppClassLoader加载

2.详解启动类加载器

上面已经讲了启动类加载器没有实体,只是将一段加载逻辑命名成启动类加载器。启动类加载器做的事情是:加载类sun.launcher.LauncherHelper,执行该类的方法checkAndLoadMain……启动类、扩展类、应用类加载器逻辑上的父子关系就是在这个方法的调用链中生成的?

1、\openjdk\jdk\src\share\classes\sun\launcher\LauncherHelper.java

 

核心代码:ClassLoader.getSystemClassLoader();

复制代码
 1 public enum LauncherHelper {
 2 ……
 3     private static final ClassLoader scloader = ClassLoader.getSystemClassLoader();
 4 ……
 5     public static Class<?> checkAndLoadMain(boolean printToStderr,
 6                                             int mode,
 7                                             String what) {
 8         ……
 9         mainClass = scloader.loadClass(cn);
10         ……
复制代码

 

 

2、\openjdk\jdk\src\share\classes\java\lang\ClassLoader.java

 

核心代码:sun.misc.Launcher.getLauncher();

复制代码
 1   public static ClassLoader getSystemClassLoader() {
 2         initSystemClassLoader();
 3         if (scl == null) {
 4             return null;
 5         }
 6         SecurityManager sm = System.getSecurityManager();
 7         if (sm != null) {
 8             checkClassLoaderPermission(scl, Reflection.getCallerClass());
 9         }
10         return scl;
11     }
12 
13     private static synchronized void initSystemClassLoader() {
14         if (!sclSet) {
15             if (scl != null)
16                 throw new IllegalStateException("recursive invocation");
17             sun.misc.Launcher l = sun.misc.Launcher.getLauncher();
18         ……
复制代码

 

3、\openjdk\jdk\src\share\classes\sun\misc\Launcher.java

 

核心代码:

  • private static Launcher launcher = new Launcher();
  • extcl = ExtClassLoader.getExtClassLoader();
  • loader = AppClassLoader.getAppClassLoader(extcl);
  • Thread.currentThread().setContextClassLoader(loader);
复制代码
 1 public class Launcher {
 2     private static URLStreamHandlerFactory factory = new Factory();
 3     private static Launcher launcher = new Launcher();
 4     private static String bootClassPath =
 5         System.getProperty("sun.boot.class.path");
 6 
 7     public static Launcher getLauncher() {
 8         return launcher;
 9     }
10 
11     private ClassLoader loader;
12 
13     public Launcher() {
14         // Create the extension class loader
15         ClassLoader extcl;
16         try {
17             extcl = ExtClassLoader.getExtClassLoader();
18         } catch (IOException e) {
19             throw new InternalError(
20                 "Could not create extension class loader", e);
21         }
22 
23         // Now create the class loader to use to launch the application
24         try {
25             loader = AppClassLoader.getAppClassLoader(extcl);
26         } catch (IOException e) {
27             throw new InternalError(
28                 "Could not create application class loader", e);
29         }
30 
31         // Also set the context class loader for the primordial thread.
32         Thread.currentThread().setContextClassLoader(loader);
33     ……
复制代码

 

 

4、扩展类加载器的创建流程

复制代码
 1 public static ExtClassLoader getExtClassLoader() throws IOException
 2         {
 3        ……
 4                             return new ExtClassLoader(dirs);
 5   ……
 6   
 7   public ExtClassLoader(File[] dirs) throws IOException {
 8             super(getExtURLs(dirs), null, factory);
 9         }
10         
11    URLClassLoader(URL[] urls, ClassLoader parent,
12                    AccessControlContext acc) {
复制代码

 

 

第二个参数传的是null,其实就是parent=null

 

5、应用类加载器的创建流程

复制代码
 1 public static ClassLoader getAppClassLoader(final ClassLoader extcl)
 2             throws IOException {
 3     final String s = System.getProperty("java.class.path");
 4     final File[] path = (s == null) ? new File[0] : getClassPath(s);
 5 
 6     // Note: on bugid 4256530
 7     // Prior implementations of this doPrivileged() block supplied
 8     // a rather restrictive ACC via a call to the private method
 9     // AppClassLoader.getContext(). This proved overly restrictive
10     // when loading  classes. Specifically it prevent
11     // accessClassInPackage.sun.* grants from being honored.
12     //
13     return AccessController.doPrivileged(
14         new PrivilegedAction<AppClassLoader>() {
15             public AppClassLoader run() {
16                 URL[] urls =
17                     (s == null) ? new URL[0] : pathToURLs(path);
18                 return new AppClassLoader(urls, extcl);
19             }
20         });
21 }
22         
23 AppClassLoader(URL[] urls, ClassLoader parent) {
24     super(urls, parent, factory);
25 }
复制代码

 

 

应用类、扩展类加载器的父子关系就是这样建立的

3.什么是双亲委派

.Class--->AppClassLoader---->ExtClassLoader--->BootStrapClassLoader

findLoadedClass 有则返回 无则委派parent (ExtClassLoader,BootStrapClassLoader非父子关系 )

通过委派还是没有 那么会通过findClass获得

双亲委派主要体现在 loadClass中

复制代码
 1 protected Class<?> loadClass(String name, boolean resolve)
 2         throws ClassNotFoundException
 3     {
 4         synchronized (getClassLoadingLock(name)) {
 5             // First, check if the class has already been loaded
 6             Class<?> c = findLoadedClass(name);
 7             if (c == null) {
 8                 long t0 = System.nanoTime();
 9                 //一下代码为双亲委派核心
10                 try {
11                     if (parent != null) {
12                         c = parent.loadClass(name, false);
13                     } else {
14                         c = findBootstrapClassOrNull(name);
15                     }
16                 } catch (ClassNotFoundException e) {
17                     // ClassNotFoundException thrown if class not found
18                     // from the non-null parent class loader
19                 }
20 
21                 if (c == null) {
22                     // If still not found, then invoke findClass in order
23                     // to find the class.
24                     long t1 = System.nanoTime();
25                     c = findClass(name);
26 
27                     // this is the defining class loader; record the stats
28                     PerfCounter.getParentDelegationTime().addTime(t1 - t0);
29                     PerfCounter.getFindClassTime().addElapsedTimeFrom(t1);
30                     PerfCounter.getFindClasses().increment();
31                 }
32             }
33             if (resolve) {
34                 resolveClass(c);
35             }
36             return c;
37         }
38     }
复制代码

 

 

4.打破双亲委派

如何打破双亲委派机制?

SPI机制,自定义类加载器(重写findClass,loadClass)

双亲委派: 向上委派

打破: 向下委派,不委派

典型示例: JDBC -->DriverManager

SPI:是一种服务发现机制。它通过在ClassPath路径下的META-INF/services文件夹查找文件,自动加载文件里所定义的类。这一机制为很多框架扩展提供了可能,比如在Dubbo、JDBC中都使用到了SPI机制

5.JVM中的沙箱安全机制

跟Linux的权限机制有点像

image.png


 

比如我定义了一个类名为String所在包为java.lang,因为这个类本来是属于jdk的,如果没有沙箱安全机制的话,这个类将会污染到我所有的String,但是由于沙箱安全机制,所以就委托顶层的bootstrap加载器查找这个类,如果没有的话就委托extsion,extsion没有就到aapclassloader,但是由于String就是jdk的源代码,所以在bootstrap那里就加载到了,先找到先使用,所以就使用bootstrap里面的String,后面的一概不能使用,这就保证了不被恶意代码污染

 

image.png

 

来源:https://www.cnblogs.com/seven-liang/p/14158463.html


相关教程