localClass类加载过程的步骤
加载->验证->准备->解析->初始化->使用->卸载
加载:在硬盘上查找并通过IO读入字节码文件,使用到类时才会加载,也就是说会把new出的对象存放到内存
验证:验证字节码文件的格式是否正确
准备:给类的静态变量分配内存,并赋予默认值
解析:将符号引用替换为直接引用
初始化:对类的静态变量进行初始化为指定的值,执行静态代码块
类加载器
引导类加载器:负责加载支撑JVM运行的位于JRE的lib目录下的核心类库,也就是JDK自身携带的类,比如rt.jar
扩展类加载器:负责加载支撑JVM运行的位于JRE的lib目录下的ext扩展目录中的JAR类包
应用程序类加载器(父加载器是扩展类加载器):负责加载ClassPath路径下的类包,主要就是加载的是自己写的那些类
自定义加载器:负责加载用户自定义路径下的类包
引导类加载器打印出来为null呢?
System.out.println(String.class.getClassLoader())
因为引导类加载器生成的对象是C++生成的,所以Java打印出来是null
类加载器的初始化过程
首先会创建JVM启动器实例:sum.misc.Launcher。sum.misc.Launcher初始化使用了单例模式设计,保证一个JVM虚拟机内只有一个sum.misc.Launcher实例。在sum.misc.Launcher构造方法内部,创建了两个类加载器,分别是扩展类加载器sum.misc.Launcher.ExtClassLoader和应用类加载器sum.misc.Launcher.AppClassLoader。JVM默认使用Launcher的getClassLoader()方法返回类加载器AppClassLoader的实例加载我们的应用程序
双亲委派机制
JVM的双亲委派机制是这这样的。要加载类会先从应用程序类加载器中查看是否这个类已经在应用类上加载过了,如果没有加载好,它会先往上去扩展类加载器上查看,如果还没加载好,那么它会往引导类加载器上去加载;如果引导类加载器上没有这个类,无法加载到的话,它就会让子加载器自己加载
其实简单一点说就是:先找父亲加载,不行再由儿子自己加载
为什么是从应用类加载器开始加载而不是从引导类加载器?
首先从代码层面考虑,JVM提供的代码就是先从应用类加载器最先开始加载,其次我们写的代码类基本上95%以上都是在应用类加载器上面的;第一次确实要多走一些步骤,但是走过一圈加载之后,就可以直接在应用程序类加载器上面找到
为什么要设计双亲委派机制?
沙箱安全机制:自己写的java.lang.String.class类不会被加载,这样可以防止核心API库被随意篡改
避免类的重复加载:当父加载器已经加载了该类时,子类就不会再加载了,保证被加载类的唯一性