类加载器

jvm支持两种类型的加载器,分别为引导类加载器(Bootstrap ClassLoader)和自定义类加载器(User-Defined ClassLoader)
从概念上来说,自定义加载器一般指的是程序中由开发人员自定义的一类类加载器,单java虚拟机规范缺没有这么定义,而是将所有派生与抽象类ClassLoader的类加载期都划分为自定义加载器。
常见的有4种类加载器:

  • 引导加载器(Bootstrap ClassLoader)
    • 加载java的核心类库(JAVA_HOME/jre/lib/rt.jar、resourecs.jar或sum.boot.class.path路径下的内容),用于提供jvm自身需要的类
    • 由C++实现,并不继承java.lang.ClassLoader,嵌套在jvm内部
    • 加载扩展类加载器和应用程序类加载器,并指定为他的父类加载器
    • 出于安全考虑,Bootstrap启动类加载器只加载包名为java、javax、sun等开头的类。
  • 扩展类加载器(Extensions ClassLoader)
    • 它负责加载JRE的扩展目录,jre/lib/ext目录下或者由java.ext.dirs系统属性指定的目录中的JAR包的类。
    • 由Java语言实现,是sun.misc.Launcher的内部类,父加载是java.net.URLClassLoader,间接继承java.lang.ClassLoader
  • 系统(应用程序)类加载器(System/Application ClassLoader)
    • 加载环境变量classpath或系统属性java.class.path指定路径下的类库
    • 程序中默认的类加载,一般来说,java应用中的类都由他来加载
    • 由Java语言实现,是sun.misc.Launcher的内部类,父加载是java.net.URLClassLoader,间接继承java.lang.ClassLoader
  • 自定义类加载器(User Defined ClassLoader)
    • 意义
      • 隔离类加载
        • 在多模块,多中间件应用中,来解决类冲突,模块之间,类加载的独立
      • 修改类加载的方式
        • 个性化加载,动态加载,不想通过扩展类加载器和系统类加载器
      • 扩展加载源
        • 比如数据库
      • 防止源码泄漏
        • 加密
    • 实现
      • 可以继承URLClassLoader
      • 可以继承ClassLoader,重载findClass方法,不破坏双亲委派机制

类加载器关系

sun.misc.Launcher,它是一个java虚拟机的入口应用

  • abstract ClassLoader 抽象类
    • loadClass(String) 调用入口,内部调用父类的loadClass和findClass
    • findClass(String) 本类获取类对象,调用defineClass
    • defineClass(byte[],int,int) 从二进制字节中获取类对象
    • resolveClass(Class<?>) 解析类对象
    • SecureClassLoader
      • URLClassLoader
        • findClass(String)
        • ExtClassLoader 扩展类加载器
        • AppClassLoader 系统类加载器
          • loadClass(String) 加载类,会调用上层加载器

类加载器直接是包含关系,上层包含下层,下层加载的类,上册是不知道的。

  • 引导加载器 由于是本地方法,所以无法获取地址和对象,加载
    • 扩展类加载器
      • 系统(应用程序)类加载器
        • 自定义类加载器(不破坏双亲委派机制的情况下,自定义一般属于最下层)

双亲委派机制

  • 防止重复加载类
    • 加载器加载时,入口是在最下层的,逐层向上委托
    • 上层加载过,下层就不会加载
    • 上层无法加载,下层才会自己去加载
  • 防止class被篡改
    • 这样导致重新写一个与java核心一样的同包同名的类是无法被加载的
      • java.lang.String 沙箱安全机制
    • 加密过的class,也无法被加载,需自定义加载器
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
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
} else {
c = findBootstrapClassOrNull(name);//调用引导类加载器
}
} 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);//本类获取

// 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的路径

  • 获取当前类的ClassLoader
    • clazz.getClassLoader()
  • 获取当前线程上下文的ClassLoader
    • Thread.currentThread().getContextClassLoader()
  • 获取系统的ClassLoader
    • ClassLoader.getSystemClassLoader()
  • 获取调用者的ClassLoader
    • DriverManager.getCallerClassLoader()

class对象是否为同一个类

必要条件:

  • 类的完整类名必须一致,包括包名
  • 加载这个类的ClassLoader(实例对象的ClassLoader)必须相同

类的主动使用和被动使用

  • 主动
    • 创建类的实例
    • 访问某个类或接口的静态变量,或者对该静态变量赋值
    • 调用类的静态方法
    • 反射
    • 初始化一个类的子类
    • java虚拟机启动时被表名为启动类的类
    • jdk7开始童工动态语言支持
      • java.lang.invoke.MethodHandle实例的解析结果REF_getStatic、REF_putStatic、REF_invokeStatic句柄对应的类没有初始化,则初始化
  • 被动
    • 除了以上主动,其余都是被动,都不会导致类的初始化