- 类加载器子系统负责从文件系统或者网络中加载Class文件,class文件在文件开头必须有特定的文件标识
- 类加载器子系统只负责class文件的加载,至于是否能够运行,则有执行引擎决定
- 加载的类信息存放于一块称为方法去的空间,除了类信息以外,方法区还会存放运行时的常量池信息,也可能包括字符串的字面量和数字常量(这部分常量信息是class文件中常量池的内存映射)
class文件被加载到 jvm中,被称为DNA元数据模板,放在方法区,然后才可以调用该类的构造器,在堆中创建class的实例
类的加载过程分为:
- 加载
- 链接
- 初始化
加载
loading加载:
- 通过一个类的权限限定名获取此类的二进制字节流
- 将这个字节流所代表的静态存储结构转化为方法区的运行时数据结构(类会放在方法区中,jdk7以前叫做永久带,jdk8叫做元空间)
- 在内存中生成一个代表这个类的java.lang.Class对象,Class的实例对应着当前加载到内存中的二进制字节流本身,作为方法区这个类的各种数据的访问入口
加载.class文件的方式:从本地获取、 通过网络、zip、运行时计算生成(例如动态代理)、其他文件生成(例如jsp)等
链接
分为:
- 验证:确保class文件的字节流包含信息符合当前虚拟机要求,保证被加载类的正确性,不会危害虚拟机自身的安全;主要包括四种验证:文件格式验证,元数据验证,字节码验证,符号引用验证
- 准备:
- 给类分配内存并且设置变量的默认初始值,
- 这里不包括用final修饰的static,因为final在编译的时候就分配了(被final修饰就是常量了),准备阶段会显示初始化
- 这里也不会实例变量分配初始化,类变量的分配在方法区中,而实例变量会随着对象一起分配到java堆中
- 解析:
- 将常量池内的符号引用转换为直接引用的过程
- 解析操作往往伴随着jvm在执行完初始化之后再执行
- 解析动作主要针对类或接口、字段、类方法、接口方法等
初始化
- 初始化阶段就是执行类构造器方法< clinit >的过程,这个方法不需要定义,只需要类中有静态的属性或者代码块即可,javac编译器会自动收集类的所有变量的赋值动作和静态代码块中的语句合并而来
- 语句按照源文件出现的顺序执行
- 如果该类有父类,jvm会保证子类的< clinit >在执行前,执行父类的< clinit >
- 虚拟机必须保证一个类的< clinit >方法在多线程情况下被加锁,类只需要被加载一次
类加载器分类:
类加载分为:
- 引导类加载器
- 自定义加载器(jvm规定凡是继承了ClassLoader的类都叫做自定义类加载器,所以扩展类加载器和系统类加载器也算是系定义类加载器)
引导类加载器不是java编写的,java核心类库都是引导类加载器加载的,
其他加载器是java编写的
jvm自带的类加载器有:引导类加载器,扩展类加载器,系统类加载器
-
引导类加载器(启动类加载器)
- 由c/c++语言实现,嵌套在jvm内部
- 用来加载java核心类库,用于提供jvm自身需要的类
- 不继承ClassLoader,没有父加载器
- 负责加载扩展类加载器和应用类加载器(类加载器也是核心类库,类加载器加载类加载器),并指定他们的父类加载器
- 出于安全考虑,引导类加载器只加载包名为:java,javax,sun等开头的类
-
扩展类加载器:
- java编写,派生于(间接继承)ClassLoader
- 父类加载器是启动类加载器(并不是继承,而是由什么加载器加载)
- 如果用户创建的jar放在jre/lib/ext目录下,也会由扩展类加载器加载
-
系统加载器(应用程序加载器)
- java语言编写,派生于ClassLoader
- 父类加载器是扩展类加载器(并不是继承)
- 是程序中默认的类加载器,一般来说,java应用的类都是由他加载