1. 类加载的时机
· 隐式加载 new 创建类的实例,
· 显式加载:loaderClass,forName等
· 访问类的静态变量,或者为静态变量赋值
· 调用类的静态方法
· 使用反射方式创建某个类或者接口对象的Class对象。
· 初始化某个类的子类
· 直接使用java.exe命令来运行某个主类
2. 类加载的过程
java编译器将 .java 文件编译成扩展名为 .class 的字节码文件。.class 文件中保存着java转换后,虚拟机将要执行的指令。当需要某个类的时候,java虚拟机会加载 .class 文件,并创建对应的class对象(元数据),将class文件加载到虚拟机的内存,这个过程被称为类的加载。
加载:
ClassLoader查找加载类对应的.class字节码文件,并利用字节码文件创建一个class对象。
验证:
目的在于确保class文件的字节流中包含信息符合当前虚拟机要求,不会危害虚拟机自身的安全,主要包括四种验证:文件格式的验证,元数据的验证,字节码验证,符号引用验证。
准备(*):
为静态变量分配内存地址,并初始化为默认的零值。这里不包含final修饰的static ,因为final在编译的时候就已经分配了。
解析(*) :
将常量池中的字符引用替换为直接引用。
初始化:
执行其静态初始化器(静态代码块)和静态初始化成员变量。
forName和loaderClass区别
- Class.forName()得到的class是已经初始化完成的。
- Classloader.loaderClass得到的class是还没有链接(验证,准备,解析三个过程被称为链接)的。
3. 双亲委派机制
类加载器的组织构造:
类加载器之间的关系:
1)BootstrapClassLoader(启动类加载器)是在JVM启动后初始化的;
2)BootstrapClassLoader负责启动ExtClassLoader(扩展类加载器),并且将ExtClassLoader的父加载器设置为BoortstrapClassLoader。
3)ExtClassLoader负责启动AppClassLoader(应用类加载器),并且将AppClassLoader的父加载器指定为ExtClassLoader。
类加载器的隔离问题:
每个类装载器都有一个自己的命名空间用来保存已装载的类。当一个类装载器装载一个类时,它会通过保存在命名空间里的类全局限定名进行搜索来检测这个类是否已经被加载了。
而JVM对类的唯一识别是 ClassLoader id
+ PackageName
+ ClassName,因此一个运行程序中完全有可能出现两个包名和类名完全一致的类。并且如果这两个类不是由一个ClassLoader加载,是无法讲一个类的实例强制转换为另一个类的,这就是ClassLoader的隔离性。
双亲委托机制:
核心思想:1)自下而上检查类是否已加载;2)自顶向下尝试加载类。
具体的实现:
1)当AppClassLoader
加载一个class
时,它首先不会自己去尝试加载这个类,而是把类加载请求委派给父类加载器ExtClassLoader
去完成。当ExtClassLoader
加载一个class
时,它首先也不会自己去尝试加载这个类,而是把类加载请求委派给BootStrapClassLoader
去完成。
2)如果BootStrapClassLoader
加载失败,会使用ExtClassLoader
来尝试加载;如果ExtClassLoader
也加载失败,则会使用AppClassLoader
来加载,如果AppClassLoader
也加载失败,则会报出异常ClassNotFoundException
。