一、概述
网上有许多介绍jvm的文章和视频,但大多枯燥、晦涩难懂,很难让小白坚持下去;如果你是一个小白,不用担心,在本文中,我们将从0到1,一步步地介绍JVM的基本概念和工作原理。无论你是想深入了解Java语言,还是准备参加Java开发的面试,这篇文章都将为你提供有用的知识和技能。让我们一起开始这段有趣的旅程吧!
二、正文
类加载器
idea编译后的.class文件,通过类加载器将之加载到JVM内存中,并放在运行时数据区的方法区内,然后再创建相对应的java.lang.Class对象,用来封装类在方法区内的数据结构。
类加载器种类
类加载器名称 | 说明 |
Bootstrap ClassLoader 【启动类加载器】 | 1、采用c/c++实现,嵌套在jvm内部 2、加载Java核心类库(JAVA_HOME/jre/lib/rt.jar),用于提供JVM自身需要的类 3、无法直接访问 |
Extension ClassLoader 【扩展类加载器】 | 1、采用java语言编写 2、加载(JAVA_HOME/jre/lib/ext)下类库 3、父类加载器:启动类加载器 |
Application ClassLoader【应用程序类加载器】 | 1、采用java语言编写 2、加载“classpath”环境变量所指定的路径中的类,可以理解为加载我们自己写的Java代码,以及我的导入的三方Jar包中的代码 3、父类加载器:扩展类加载器 |
自定义类加载器 | 1、继承ClassLoader类,可定制类的加载方式 |
类加载时采用了双亲委派模式,什么是双亲委派模式
当一个类加载器收到类加载的请求时,自己不会立刻去加载这个类,而是把加载请求委派给父类加载器完成,父类加载器也会做相同的处理;当父加载器在自己的搜索范围内找不到需要加载的类时,子加载器才会尝试自己去加载,最后如果任何一个加载器都找不到这个类时,就会抛出:ClassNotFoundException异常。
classloader源码分析
public Class<?> loadClass(String name) throws ClassNotFoundException {
return loadClass(name, false);
}
protected Class<?> loadClass(String name, boolean resolve) throws ClassNotFoundException {
synchronized (getClassLoadingLock(name)) {
// 检查一下指定名称的类是否已经加载过,如果加载过了,就不需要再加载,直接返回
Class<?> c = findLoadedClass(name);
if (c == null) {
long t0 = System.nanoTime();
try {
// 如果该类没有加载过,则判断是否存在父类加载器;
// 如果存在父类加载器,则由父类加载器进行加载
// 递归调用,父类加载器会进行相同判断处理
if (parent != null) {
c = parent.loadClass(name, false);
} else {
// 【注意】如果没有父类,则委托给启动加载器去加载
c = findBootstrapClassOrNull(name);
}
} catch (ClassNotFoundException e) {
// ClassNotFoundException thrown if class not found
// from the non-null parent class loader
}
if (c == null) {
long t1 = System.nanoTime();
// 如果都没有找到,则通过自定义实现的findClass去查找并加载
c = findClass(name);
// this is the defining class loader; record the stats
sun.misc.PerfCounter.getParentDelegationTime().addTime(t1 - t0);
sun.misc.PerfCounter.getFindClassTime().addElapsedTimeFrom(t1);
sun.misc.PerfCounter.getFindClasses().increment();
}
}
// 是否需要在加载时进行解析(根据入参指定)
if (resolve) {
resolveClass(c);
}
return c;
}
}
为什么需要双亲委派模式
假如没有双亲委派机制,此时用户编写一个和jdk自带的一模一样的类(比如:java.lang.Object),并放到了Classpath中,那么应用程序类加载器就把这个当做Object加载到了内存中,从而造成不可预知的后果;而双亲委派机制先一路向上委托,启动类加载器去找的时候,就把正确的Object加载到了内存中,后面再加载用户编写的Object类时,是不会加载运行的。
所以:JDK自带类是无法被覆盖的,而引入第三方的JAR是可以自己定义相同的类来覆盖的。