类加载子系统的作用
类加载子系统就是负责加载字节码class文件.类加载的信息存放于方法区的内存空间.类加载子系统就是负责class文件的加载,对于是否可以运行则由执行引擎这部分决定.
class文件在其文件开头有特定的文件标识(以 CA FE BA BE 标识开头).
类加载的过程
如图所示为类加载的具体过程.
加载阶段:
在这个阶段,会通过类名获取此类的二进制字节流,然后将这个字节流所代表的静态存储结构转换为方法区的运行时结构,会在内存中生成一个代表这个类的java.lang.Class对象,作为方法区中这个类的各种数据的访问入口.
链接阶段:
在这个阶段又可以分为验证,准备和解析来进行理解.其中验证阶段,顾名思义,是进行检验被加载的类是否是正确的内部结构,并和其他类协调一致.准备阶段,是负责为类的静态属性分配内存,并设置默认的初始值,这里初始化不包含被final修饰的static实例变量,它在编译时进行初始化,也不会为实例变量初始化.解析是将类的二进制数据中的符号引用替换成直接引用.
初始化阶段:
类在以下情况会进行初始化:
1.创建类的实例,new对象
2.访问某个类或者接口的静态变量,或者对该静态变量进行赋值
3.调用类的静态方法
4.反射,比如连接数据库会用到的(Class.forName(""))
5.初始化一个类的子类,会先初始化子类的父类
类的初始化顺序:
对于同时包含多个静态变量和静态代码块,按照自上而下的顺序
对于初始化一个类,如果父类没有初始化,会先初始化父类
所以顺序为:父类static->子类static->父类构造方法->子类构造方法
类的主动/被动使用
类的主动使用和被动使用的区别在于类是否会被初始化.
除了上述会进行初始化的主动使用,剩下的就为被动使用了.
比如:引用该类的静态常量,这里的常量是指已经指定的常量,对于需要计算才会得到结果的常量会导致初始化.
构造某个类的数组时不会导致该类初始化.
类加载器分类
jvm支持的两种类加载器分别是引导类加载器和自定义类加载器.在java虚拟机规范中将所有派生于抽象类的类加载的类加载器都划分为自定义类加载器.
如图所示为常见的类加载器:
引导类加载器(启动类加载器)
使用C/C++语言实现的类加载器,用来加载java核心类库.没有父加载器.负责加载扩张类加载器和应用类加载器,并为他们的父类加载器.
引用类加载器只加载包名为java,javax,sun等开头的类.
扩展类加载器
使用java语言编写的,由sun.misc.launcher$ExtClassLoader实现.派生于ClassLoader类,上层类加载器为引用类加载器.
从java.ext.dirs系统属性所指定的目录中加载类库,或从JDK系统安装目录的jre/lib/ext子目录下加载类库.如果用户创建的jar放在此目录下也会自动由扩展类加载器加载.
应用程序类加载器
使用java语言编写的,由sun.misc.launcher$AppClassLoader实现.派生于ClassLoader类,上层类加载器为扩展类加载器.加载我们自己定义的类.
ClassLoader类是一个抽象类,其后所有类加载器都继承子此类,除了启动类加载器.
双亲委派机制
Java虚拟机采用的双亲委派模式,进行类加载过程,将请求交给父类处理.
具体过程:当加载一个类时,并不会自己先进行加载,而是把这个请求委托父类的加载器执行,如果父类加载器还存在父类加载器,则会继续向上委托,如果父类可以完成类加载的请求,则成功返回,若果无法完成,子加载器才会自己去进行加载.若果均失败,会抛出ClassNotFoundException异常.
优点:安全,可以避免用户自编写的类替换核心类;避免全限定命名的类重复加载.
沙箱安全机制
是为了防止恶意代码污染源代码.
举个栗子,如果我们自定义一个类,为String,包名为java.lang,如果没有沙箱安全机制,这个类就会污染系统中的类,有了这个机制,就会委托顶层的引导类加载器查找这个类,如果没有在向下找,String类是jdk的源代码,所以引导类加载处就先加载到,后面的一概不能使用,这就保证了安全.