JVM-类加载子系统
1.类加载子系统的作用
- 从文件系统或者网络中加载字节码文件(.class后缀)
- 将字节码文件的信息存放到JVM运行时数据区的方法区中,将字节流所代表的静态存储结构转化为方法区的运行时数据结构
- 在内存中生成代表该class文件对应类的java.lang.Class对象,作为该类在方法区的数据入口
2.类加载器的执行过程
- 加载:通过类文件的全限定名获取该文件对对应的二进制字节流,将字节流所代表的静态存储结构转化为JVM运行时数据结构,并在内存中生成对应的java.lang.Class对象,作为方法区这个类的各种数据访问入口
- 链接
- 验证:确保Class文件字节流中所包含的信息符合当前虚拟机要求
- 准备:为类变量在方法区中分配内存并赋默认值
- 解析:将常量池内符号引用转化为直接引用
- 初始化
- 执行类构造器方法()的过程
- ()方法不需要定义,也不同于类的构造器,该方法是javac编译器在编译时期自动收集类中的所有变量赋值操作和静态代码块合并而来的
3.类加载器分类
-
引用类加载器(Bootstrap ClassLoader)
该类使用C/C++语言实现,用来加载Java的核心库(JAVA_HOME/jre/lib/rt.jar、resources.jar或sun.boot.class.path路径下的资源),用于提供JVM自身需要的类(只加载包名为java、javax、sun开头的类)
-
自定义类加载器(User-Define ClassLoader)
-
扩展类加载器(Extension ClassLoader)
sun.misc.Launcher$ExtClassLoader类实现,加载java.ext.dirs系统属性所指定的目录或JDK的安装目录的jre/1ib/ext子目录
-
系统类加载器 (Application ClassLoader)
sun.misc.LaunchersAppClassLoader类实现,加载环境变量classpath或系统属性java.class.path指定路径下的类库,Java应用的类都是由它来完成加载
-
用户自定义类加载器
开发人员可以通过继承抽象类ava.lang.ClassLoader类的方式,实现自己的类加载器,如果没有太过于复杂的需求,可以直接继承URLClassLoader类,这样就可以避免自己去编写findClass() 方法及其获取字节码流的方式,使自定义类加载器编写更加简洁。
-
4.双亲委派机制
Java虚拟机对class文件采用的是按需加载的方式,加载class文件采用的是双亲委派机制
原理:
- 类加载器收到类加载请求,判断是否有父类加载器,如果有父类加载器委托给父类加载器处理
- 父类加载器进行上述递归操作,最终请求到顶层的启动类加载器
- 如果父类加载器可以完成加载任务,加载成功返回,如果父类加载器无法完成类的加载,再重新交给自类加载器去加载
优势:
- 避免类的重复架加载
- 保护程序安全,防止核心API被随意篡改
5.类的使用
一个类经历过完整的加载、链接和初始化3个类加载步骤后,开发人员可以在程序中访问
和调用该类的静态成员信息,包括静态变量和静态方法,或者使用new关键字为该类创建对象实例
6.类的卸载
类、类加载器、实例、类的Class对象三者之间的关系
- 类加载器内部通过Java集合存放类加载器所加载的类的引用
- 一个Class对象总是会引用它的类加载器,通过调用Class对象的getClassLoader()方法获得该类的类加载器
- 一个类的实例通过getClass()方法获取类的Class对象引用
- 类的静态属性class引用该类的Class对象
类的生命周期
一个类被加载、链接和初始化后,该类的生命周期就已经开始了,当该类的Class对象不再被引用时,表示该类的生命周期已经结束,一个类何时结束生命周期,取决于该类的Class对象何时结束生命周期
类的卸载
- 启动类加载的类在整个运行期间是不可能被卸载的
- 被系统类加载器和扩展类加载的类运行期间不太可能被卸载,因为系统类加载器实例或者扩展类的实例基本上在整个运行期间总能直接或者间接的访问到
- 自定义类加载器加载的类只有在简单的上下文环境中才能被卸载,一般要借助虚拟机的垃圾收集功能才可以做到
方法区的垃圾回收
-
废弃常量
常量池中的常量没有被任何地方应用,就可以被回收
-
不再使用的类型
- 该类的所有实例都已经被回收
- 装载该类的类加载器已经被回收
- 该类对应的java.lang.Class对象没有被任何地方引用,也就是无法在任何地方通过反射访问该类的方法