好久不见朋友们,又到了更新文章的时候了,我是你们的好朋友,有一丢丢黑,还好没有你们想象中的那么黑。
(如果图片有侵权,请联系本人)
今天我们来聊一下java的类加载机制,这个问题适合刚了解jvm的朋友们
首先我们要运行一个class, 肯定是要先用 javac编译一个类,然后然后再用java命令运行class文件
javac Test.java -> java Test 这样就可以运行class文件了
加载一个类一共要五个步骤
加载 -> 验证 -> 准备 -> 解析 -> 初始化
首先我们说一下加载,加载就是把class的字节码文件加载到内存,这个字节码文件中包含一个一个的指令
我们先随便来创建一个类
public class SynchronizedTest {
public static void main(String[] args) {
synchronized (SynchronizedTest.class) {
int i = 1;
System.out.println(1);
}
}
public synchronized static void test(){
int i1 = 1;
}
public synchronized void test2(){
int i2 = 1;
}
}
然后我们在编译之后执行 javap -v SynchronizedTest
可以看到字节码
首先第一步就是把这样的一个字节码文件加载到内存中
然后下一步就是验证,每一个class文件都有一个魔法数,就是CAFEBABE开头的,可见我们的Java设计者还是很有情趣的,用咖啡宝贝开头
接下来就到了准备阶段,把不包含final的static常量设置一个初始值
比如:
package com.smile;
public class StaticTest {
public static int i ;
public static void main(String[] args) {
System.out.println(i);
}
}
打印结果就是0
下一步就是解析,就是把符号引用替换成直接引用
什么是符号引用, 符号引用比如类引用就是 java/lang/Integer 方法引用就是 java/lang/Integer#parseInt()I 字段引用就是 java/lang/System#out
什么是直接引用
直接引用就是指向内存地址或者存储的位置
我们知道大部分类对象都是在堆内存中开辟一块内存空间,直接引用就是引用这块内存空间的地址 或者每一个方法区的方法都有一个入口地址,这个入口地址属于直接引用 还有每一个字段在对象中都有一个偏移量,这个偏移量也属于直接引用
对于java内存模型这一块,之后我们有时间再详细去讲
接下来就是初始化
初始化,比如new啊,这些指令,或者比如执行父类的静态代码块,反射调用等。
然后我们来说说java的类加载器
java的类加载器一共有三种,分别是
BootstrapClassLoader主要是加载JAVA_HOME/lib下面的类 然后ExtensionClassLoader类加载器主要是加载JAVA_HOME/lib/ext下的类 ApplicationClassLoader 主要是加载classpath下的类
来看一下下面的代码
package com.smile;
public class ClassLoaderTest {
public static void main(String[] args) {
ClassLoader classLoader = ClassLoader.getSystemClassLoader();
System.out.println(classLoader.getParent());
System.out.println(classLoader.getParent().getParent());
}
}
打印结果为:
可见 最上层的是应用类加载器ApplicationClassLoader,其次是扩展类加载器ExtensionClassLoader,最后是启动类加载器BootstrapClassLoader
说到类的加载机制,就不得不提到双亲委派模型,以及tomcat是怎么打破双亲委派模型的呢,想了解双亲委派模型的盆友们,请听下回分解,点关注,点收藏,点关注,点转发,一键三连,谢谢大家