- 类加载器子系统负责从文件系统或者网络中加载class文件,class文件在文件开头有特定的文件标识
- classloader只负责class文件的加载,至于它是否可以运行,则由Execution Engine决定
- 加载的类信息存放在名为方法区的内存空间。除了类的信息外方法区还会存放运行时常量池信息
加载器执行阶段
一、加载阶段
- 通过类的全限定名获取定义此类的二进制字节流
- 将这个字节流所代表的的静态存储结构转化为方法区的运行时数据结构
- 在内存中生成一个代表这个类的java.lang.Class对象,作为方法区这个类的各种数据的访问入口。
二、连接阶段
1、验证(Verify)
- 目的在于保证class文件的字节流中包含信息符合当前虚拟机的要求,保证被加载类的正确性,不会危害虚拟机自身安全
- 主要包含四种验证,文件格式验证,原数据验证,字节码验证,符号引用验证。
2、准备(Prepare)
- 为类变量分配内存并且设置该类变量的默认初始值,即零值。
- 这里不包含用final修饰的,因为final在编译时就被分配了,准备阶段会显式的初始化赋值。
- 此时不会为实例变量分配初始化,类变量会分配到方法区中,而实例变量是会随对象一起分配到java堆中
3、解析(Resolve)
- 将常量池内的符号引用转换为直接引用的过程
- 事实上,解析操作往往会伴随着jvm执行完初始化之后再执行
- 符号引用就是一组符号来描述所引用的目标。直接引用就是指向目标的指针、相对偏移量或一个间接定位到目标的句柄。
- 解释动作主要针对类或接口、字段、类方法、接口方法、方法类型等
三、初始化阶段
- 初始化阶段就是执行class文件中类构造器方法<clinit>()的过程。
- <clinit>()不需要定义,其内容为静态变量的赋值 动作与静态代码块中的内容两部分组成。无静态变量与静态代码块则无<clinit>()
- <clinit>()中指令安装语句在源文件中出现的顺序执行
- 若该类有父类,会先执行父类的<clinit>()
- 虚拟机保证一个类得<clinit>()方法在多线程下同步加锁,即只执行一次
加载器类型
启动类(引导类)加载器(bootstrap classloader)
- 这个类是用C/C++实现的,嵌套在JVM内部、没有父类加载器
- 用来加载包名为java、javax、sun等开头的类
- 可以用来加载拓展类和应用类加载器,并指定为他们的父类加载器
拓展类加载器(Extension ClassLoader)
- 由java语言编写
- 派生于ClassLoader类
- 父类加载器为启动类加载器
- 加载jre/lib/ext目录下的类库,如果用户创建的jar放到此目录下,也会自动由拓展类加载器加载
应用程序类(系统类)加载器(AppClassLoader)
- java语言编写
- 派生于ClassLoader类
- 父类加载器为拓展类加载器
- 负责加载环境变量classpath或系统属性java.class.path指定路径下的类
- 该类是程序中默认的类加载器,java应用的类都是由他完成加载
- 通过ClassLoader#getSystemClassLoader()可以获取该类加载器
自定义类加载器
- 可以支持一些个性化的扩展功能。
- 父类加载器为应用程序加载器
JVM必须知道一个类是由启动类加载器还是由用户类加载器加载的。类的信息会保存到方法区中,jvm会将这个类的加载器的一个引用作为类型信息的一部分保存在方法区中。
双亲委派机制(父类委托)
java对class文件采用按需加载的方式,即需要当前使用类时才会将它的class文件加载到内存生成class对象。
而且加载类时采用双亲委派模式,即把类的加载先交给父类处理
- 如果一个类加载器收到请求,他并不会自己先去加载,而是把这个请求委托给父类加载器去执行,父类加载器再委托到他的父类加载器,一直委托到引导类加载器。
- 引导类加载器如果能加载到对应类直接返回。如果加载不到就会按照员链路返回让子类加载器进行加载直到能加载到类。
优势
- 防止同报名类的重复加载
- 保护程序安全,在java包下加会提示
防止引导类加载器加载的类对加载器造成影响
沙箱安全机制
在自定义string类时会因为双亲委派机制加载核心包中的而不是自定义的,自定义核心包下的类时也会被禁止,起到保护java核心源码的作用。这就是沙箱安全机制。
判断是否是同一个类的条件
- 类的完整类名必须一致,包括包名
- 加载这个类的classloader(类加载器)必须相同
类的主动使用与被动使用
主动使用会触发类的初始化,被动使用不会
哪些是类的主动调用:
- 创建类的示例
- 使用类中的静态变量或给静态变量赋值
- 调用类的静态方法
- 反射(Class.forName(“com.atguigu.Test”))
- 初始化一个类的子类时,会主动使用他的父类
- JDK7提供的动态语言支持:
java.lang.invoke.MethodHandle示例的解析结果java.lang.invoke.MethodHandle实例的解析结果REF_getStatic、REF_putStatic、REF_invokeStatic句柄对应的类没有初始化,则初始化