类加载
类的加载指的是将类的.class文件中的二进制数据读入到内存中,将其放在运行时数据区的方法区内,然后在堆区创建一个java.lang.Class对象,用来封装类在方法区内的数据结构。
类加载过程
- 加载:加载二进制
- 连接
- 验证:验证类二进制的正确性
- 准备:
- 静态变量分配内存(清空内存),初始化默认值 static a = 0;
- 构造与该类相关联的方法表。
- 【static final变量String或基本类型赋值在准备阶段就完成了】
- 解析:
- 类中符号引用转为直接引用。
- ClassLoader.loadClass(className)执行到这里【默认不解析】,没有使用不进行初始化。
- 初始化【class对象也是对象,对象就需要初始化】:
- 静态变量赋值 static a = 3;
- 静态代码块。 (只初始化时执行一次,第二次new就没有了)
- 非静态代码块,在创建对象的时候(即new一个对象的时候)执行,每次创建对象都会执行一次
- Class.forName(className, true, this.getClass().getClassLoader())。注意第二个参数,是指Class被loading后是不是必须被初始化。
你知道Java中final和static修饰的变量是在什么时候赋值的吗?_Archie_java的博客-CSDN博客_final修饰的变量什么时候加载
构造函数是实例化阶段运行,即new。
JVM内存结构
static变量赋值
主动使用
- 创建类实例:new
- 读写类的静态变量【反例:访问static+final不初始化,准 备阶段完成】
- 调用类静态方法
- 反射:Class.forName(“xxx”)
- 初始化类的子类
- jvm启动时被标明为启动类
- ..
不会触发主动使用
- final static(String和基础变量int等)不会初始化,单static会初始化
parent.parentFinalStatic
Child.parentFinalStatic
- 单static谁的属性,初始化谁
Child.parentStatic,只初始化Parent,不会初始化Child。
- 创建数组不会初始化
ClassLoader.loadClass 和 Class.forName() 有什么区别?
- ClassLoader#loadClass(java.lang.String, boolean)
protected Class<?> loadClass(String name, boolean resolve) throws ClassNotFoundException
第二个参数resolve ,是否进行解析。这是protected方法,默认不解析。
- Class.forName()
public static Class < ?>forName(String className) throws ClassNotFoundException {
Class < ?>caller = Reflection.getCallerClass();
return forName0(className, true, ClassLoader.getClassLoader(caller), caller);
}
forName0 第二个参数为true,表示是否进行初始化。
可以传入指定的ClassLoader。
Class.forName(className)实际上是调用Class.forName(className, true, this.getClass().getClassLoader())。注意第二个参数,是指Class被loading后是不是必须被初始化。 ClassLoader.loadClass(className)实际上调用的是ClassLoader.loadClass(name, false) ,第二个参数指出Class是否被link。 java下Class.forName的作用是什么,为什么要使用它 - 青鸟飞鱼 - CSDN博客
结论:区别就是
- Class.forName默认初始化
ClassLoader.loadClass默认不解析(没有符号引用转为直接饮用)
loadClass参数最多到“解析(Resolve)”阶段,forName可以通过参数进行类“初始化”。
- loadClass不可以指定ClassLoader,forName可以指定ClassLoader,没有指定就用调用者的ClassLoader。
loadClass和Class.forName的区别 - 简书
jdbc驱动为什么用Class.forName(“com.mysql.jdbc.Driver”);,而不是放在classpath下自动加载?
Class.forName 不是为了加载类,
Class.forName 不是为了加载类,
Class.forName 不是为了加载类,而是为了执行Driver中的static 代码块。static代码块中将mysql的driver实现注册到了jdk中的DriverManager中。
然后,DriverManager#getConnection(java.lang.String, java.util.Properties, java.lang.Class<?>)就会用到注册的driver。
- JDBC学习2:为什么要写Class.forName(“XXX”)? - 五月的仓颉 - 博客园
类加载器
Jdk中的类加载器
jdk中的classloader类。
ExtClassLoader 和 AppClassLoader并不是继承关系。而是创建AppClassLoader时传递进来。自定义ClassLoader加载顺序
可以自定义ClassLoader重写loadClass 方法,
通常我们重写的是findClass方法。
loadClass先检查父classloader,然后调用findClass()方法。
但即便重写loadClass方法也不能加载自定义的java.lang.String类
因为针对java.*开头的类,jvm的实现中已经保证了必须由bootstrap来加载
SPI打破双亲委托机制?【不觉得,并没有自定义类加载器改变加载顺序】
为什么说java spi破坏双亲委派模型? - 知乎
父加载器能使用当前线程Thread.currentThread().getContextLoader() 所指定的ClassLoader来加载类【也就是父类(父加载器加载)的方法中使用子加载器加载类,通过Thread获取其ClassLoader】
DriverManager是通过BootstrapClassLoader加载的,在DriveManager中使用spi
ServiceLoader loadedDrivers = ServiceLoader.load(Driver.class);
serviceLoader是bootstrap 类加载器加载,那通过serviceLoader加载出来的类是什么加载器加载的呢?
AppClassLoader
获取线程上下文ClassLoader
spi中使用ClassLoader cl = Thread.currentThread().getContextClassLoader(); 获取当前线程的类加载器appClassLoader,来加载mysql包中的Driver类。
Tomcat ClassLoader 打破双亲委派机制?
Tomcat5 ClassLoader
- shared.loader可以配置
- Class loader in_cloud
Tomcat7 ClassLoader
tomcat7三个Classloader都是同一个实例。tomcat5是分开的。
Common ClassLoader
仅运行一个Tomcat实例时,这两个属性(catalina.home(安装目录),catalina.base(工作目录))指向的位置是相同的。 [Tomcat]使用Catalina_base部署多个tomcat实例 - 简书
Catalina也叫Server classloader。
Tomcat ClassLoader 加载顺序
tomcat5 到7 虽然ClassLoader改变,但加载顺序不变。
默认的加载顺序是
- Bootstrap classes of your JVM
- /WEB-INF/classes of your web application
- /WEB-INF/lib/*.jar of your web application
- System class loader classes (described above)
- Common class loader classes (described above)
如果设置了
- Bootstrap classes of your JVM
- System class loader classes (described above)
- Common class loader classes (described above)
- /WEB-INF/classes of your web application
- /WEB-INF/lib/*.jar of your web application
Apache Tomcat 8 (8.0.53) - Class Loader HOW-TO
官方文档中的Bootstrap = Bootstrap + Extension
Tomcat ClassLoader违反双亲委派模型
在 CATALINA_HOME/lib 以及 WEB-INF/lib 中放置了 不同版本的jar包,此时就会导致 WEB-INF/lib中覆盖CATALINA_HOME/lib中的类。导致运行错误。
maven
不懂为什么要这么设计。
tomcat类加载器为什么要破坏双亲委派机制? - 牧云文仔 - 博客园
why does tomcat classloader break up parent delegation - Stack Overflow