类加载子系统
类加载子系统是负责将.class文件加载,验证,准备,解析,初始化的。而类加载器ClassLoader就像一个快递员,将class文件加载到jvm中。类加载器属于类加载器子系统的一部分。
加载字节码文件的几种方式
- 从本地系统中直接加载
- 通过网络获取,典型场景Web Applet
- 从Zip压缩包中读取,成为日后jar,war格式的基础
- 运行时计算生成,比如:动态代理技术
- 从加密文件中获取,可以防止class文件被反编译
类加载器子系统的三个阶段:
加载->链接->初始化
加载:
- 通过一个类的全限定类名顶一次类的二进制字节流。2.将这个字节流所代表的静态存储结构转化为方法区的运行时数据结构。3.再内存中生成一个代表这个类的Class对象,作为方法去这个类的各种数据的访问入口
验证:
目的在于确保Class文件的字节流符合当前虚拟机要求,保证类的正确性,不会危害虚拟机自身安全。包括四种验证:文件格式验证,元数据验证,字节码验证,符号引用验证。
准备:
- 为类变量分配内存斌设置变量的默认初始值。
- final修饰的静态变量在编译的时候就会分配,在这个阶段直接显示初始化。
- 实例变量随着对象一起分配在堆区,这个阶段不会为实例变量分配初始化。
解析:
- 将常量池内的符号引用转换为直接引用。
- 解析动作主要针对类或接口、字段、类方法、接口方法、方法类型等。
初始化:
- 初始化阶段就是执行类构造器方法 clinit()的过程。
- 该方法是由javac编译器自动收集类中多有类变量的赋值动作和静态代码块的语句按顺序合并而来。
- 执行该方法前先要保证父类的该方法被执行过了。
- jvm必须保证一个类的该方法在多线程下被同步加锁。
类加载器
jvm支持两种类型的类加载器,分别为引导类加载器(BootStrapClassLoader)和自定义类加载器(继承自ClassLoader的类加载器)。
- 引导类加载器是使用c/c++语言实现的,自然就属于顶级类加载器,嵌套在JVM内部,它不继承ClassLoader,没有父类,在双亲委派机制中属于根节点。
- 它用来加载java的核心类库(rt.jar,resourses.jar或sun.boot.class.path路径下的内容),用于提供jvm自身需要的类,出于安全考虑,BootStrap引导类加载器只加载包名为java、javax、sun等开头的类。
- 其他的类加载器全属于自定义类加载器。其中扩展类加载器(Extension ClassLoader) 和 应用程序类加载器(AppClassLoader) 是jvm自带的,因为它继承了ClassLoader,所以可以说是自定义类加载器。
扩展类加载器是用java编写的,它的父类是引导类加载器,可以从java.ext.dirs系统属性所指定的目录中加载类库,也可以从jre/lib/ext子目录中加载类库。
应用程序类加载器是用java写的,它是类加载中默认的类加载器,父类加载器是扩展类加载器
双亲委派机制
①一个类收到类加载请求后并不会自己去加载,而实现委托给父类的加载器去执行。
②如果父类存在还存在父类加载器,则继续向上委托,知道委托给引导类加载器。
③如果父类加载器能加载则加载并返回,如果不能加载则由子类加载去尝试加载,这就是双亲委派机制。
优点:
- 避免类的重复加载
- 保护程序安全,防止核心API被随意篡改
沙箱安全机制
沙箱安全机制可以说是双亲委派机制的具体实现,我们可以自定义一个String类,类加载时自定义类先使用AppCloader加载,根据提到的的,AppCloader加载不了委托给Extension ClassLoader加载,它也加载不了,继续向上委托,BootStrapClassLoader能加载,就去先找jdk自带的java\lang\String.class,而加载String类后,调用main()方法,String类中没有main()方法就报错了。