JVM的类加载机制
类加载器初始化的简单流程图
类加载的过程简单流程图
类加载的过程:
- 加载:在硬盘上查找并通过IO读入字节码文件,使用到类时才会加载,例如调用类的main函数、new对象等。在加载阶段会在内存中生成一个代表这个类的java.lang.Class对象,作为方法区这个类的各种数据的访问入口。
- 验证:校验字节码文件的正确性。
- 准备:给类的静态变量【非常量】分配内存,并赋予默认值【整型默认0,布尔默认false,引用类型默认null】。
- 解析:把符号引用替换为直接引用,这个阶段会把一些静态方法【静态方法就是符号引用,比如main()函数】替换为指向数据所在内存的指针或句柄等【即直接引用】,这就是所谓的静态链接过程【在类加载期间完成】。动态链接是在程序运行期间完成的把符号引用替换为直接引用。
- 初始化:对类的静态变量初始化为指定的值,执行静态代码块。
类被加载到方法区后,主要包含运行时常量池、类型信息、字段信息、方法信息、类加载器的引用、对应class实例的引用等信息。
类加载过程主要是通过类加载器来实现的,Java中有以下几种类加载器:
- 引导类加载器:负责加载支撑JVM运行的位于JRE的lib目录下的核心类库,比如rt.jar、charsets.jar等。
- 扩展类加载器:负责加载支撑JVM运行的位于JRE的lib目录下的ext扩展目录中的JAR类包。
- 应用程序类加载器:负责加载ClassPath路径下的类包,主要就是加载你写的源代码。
- 自定义类加载器:负责加载用户自定义路径下的类包。
双亲委派机制
双亲委派机制即按照上图从下往上去加载类,如果应用程序类加载器找到了类就直接返回,如果找不到就向上委托给扩展类加载器继续加载,同样找到就返回,如果还找不到就继续向上委托引导类加载器继续加载,还找不到就会向下委托给扩展类加载器去加载,还加载不到又继续向下委托到应用程序类加载器加载,还找不到就会报ClassNotFound异常了。
是不是感觉有点绕?跑了一圈又回来了!
你可能会问既然要这样绕一圈,为什么不直接从引导类加载器开始加载类呢?我猜测可能是因为需要加载的类大部分都是你写的源代码,即只有应用程序类加载器才能加载到的类,既然大部分都是它才能加载到的,干脆直接从它开始加载好了。这只是我的猜想哈!更具体的原因,也许只有JVM的研发人员才知晓了吧!
为什么要设计双亲委派机制?
- 沙箱安全机制:自己写的与核心类库中类名相同的类不会被加载,比如自己写的java.lang.String.class,这样可以防止核心API库被随意篡改。
- 避免类的重复加载:当父类加载器已经加载到该类时,子类加载器就没必要再次加载,这样可以保证被加载类的唯一性。
全盘负责委托机制
“全盘负责”是指当一个ClassLoader装载一个类时,除非显示地使用另外一个ClassLoader,该类所依赖以及引用的类也由这个ClassLoader载入。
打破双亲委派机制
打破双亲委派机制就是不委托父加载器去加载类了,而是自己一次性去加载类,可以通过重写loadClass方法实现。
感谢图灵学院的诸葛老师!!