Java类加载机制
一、为什么需要有类加载机制?
通常我们的程序都是在编辑器比如eclipse上编写的,充其量也只是一些文本,这些内容和逻辑要怎么执行呢?有些人可能会说java是通过执行字节码文件来运行程序的,我们编译过后会生成字节码文件(.class文件),那请问:在执行字节码文件之前要做什么?其实就是把字节码文件加载到JVM的内存当中,只有在内存中才能运行!
二、先简单认识类要被加载到的地方——JVM内存
当我们启动一个java程序的时候,就会启动一个JVM虚拟机进程,这个进程包含所有这个程序所产生的线程,同一个JVM的所有线程、所有数据都放在了同一个JVM进程所包含的内存空间中,即共享该JVM进程的内存区域(一家人就是要在一起嘛)。不同的JVM进程不会共享内存。
三、JVM进程什么时候会结束?
当出现如下情况中的任何一种的时候,JVM进程结束:JVM进程结束的时候,所包含内存中的所有数据和状态都会丢失!
1、程序正常执行到结尾,结束;
2、程序中使用了System.exit(0)或者是Runtime.getRuntime().exit(0);中的一种语句,主动结束程序;
3、程序中发生了未捕获的异常或者是错误,导致程序结束;
4、所在平台强制结束JVM进程;
四、类加载的过程
小前言:地球人都知道,Java通过new操作符来产生对象的时候,都是会产生实例对象,“类”信息就是模板,这个“模板”在被使用之前,都必须通过类加载器(类似于搬运工,后面会讲到)加载到JVM内存中,供程序后续的操作使用。
当程序主动使用一个类的时候,JVM虚拟机会检查这个类是否被加载到了内存中,如果没有,则进行加载、连接、初始化三个步骤完成类加载!(当然JVM规范允许对类进行“预加载”,不需要等到第一次使用的时候才加载)
1、加载过程之步骤一:类的加载
类加载指的是把字节码.class文件加载到内存中,为每个字节码文件生成java.lang.Class对象(每个类其实也是一个实例,都是java.lang.Class的一个实例),程序使用任何类的时候,都会为之生成一个java.lang.Class对象,保存在JVM的内存中。
类的加载由类加载器完成,类加载器通常由JVM提供,JVM提供的类加载器一般称为系统类加载器。关于加载器体系,后面会讲到;
2、加载过程之步骤二:连接
连接阶段会把类的二进制数据合并到JRE中,类连接总共分为三个阶段执行:
(1)验证:检查被加载的类是否有正确的内部结构(规范),是否和其他类协调一致;
(2)准备:负责为类变量(static)分配内存,设置初始化值;
(3)解析:将类中的二进制数据中的符号引用改为直接引用(欢迎各路大神对这点进行评论和进一步解释);
3、加载过程之步骤三:类的初始化
此阶段主要是对类进行初始化,主要初始化:类变量。 类变量的初始化方式有两种:(1)声明时直接赋初始值、(2)使用静态初始化块进行赋值;
例如有
static int value = 5;
或者有初始化块:
static{ value = 100; }
JVM会顺序执行这些初始化的指令,static的值将会是代码中最后一次赋值时候的值;
在类的初始化的步骤通常有这三个:
(1)发现类没有被加载和连接,那么就会执行加载和连接;
(2)类有直接的父类没有被初始化,那就先执行父类的初始化;
(3)以此执行初始化语句,主要是针对类变量的初始化;(实例变量的初始化要在new对象的时候才会进行);
因为类如果有父类,则必须进行父类的初始化1-3的步骤,以此类推,程序会把使用到的类的直接、间接父类全部执行上述3个步骤,保证所有父类都被初始化,因为java.lang.Object是所有类的祖宗类,所以Object总是会被JVM最先执行上述步骤!
五、类加载器体系
1、要把类加载到内存,必须要有加载的工具——类加载器!类加载器负责从磁盘中或者网络上加载字节码文件,并在JVM内存中生成java.lang.Class对象。
2、类不会被重复加载,也不需要被重复加载,也就是说:一旦被加载后,可以无限次被使用,除非JVM进程结束!
3、在java源码中类使用包名+类名进行标识,在JVM中,类被包名+类名+加载器作为标识,所以同样路径的类被不同的加载器加载也可以被区分。
加载器体系:

bootStrap加载器称为根类加载器,负责加载系统核心类;
Extension加载器称为拓展类加载器,负责加载拓展的常用类;
System加载器就是系统加载器,通常由它来完成我们编写的类;
用户可以自定义加载器来加载字节码!
一个类的加载过程由谁负责:
首先由当前线程使用的类加载器尝试进行加载,该类加载器会先请求父类加载器进行检查,直到顶端(根类加载器),如果父类没有加载并且不能加载,则由当前加载器加载。(溯源委派模式)。
一个测试类,获取当前类加载器,打印出父类加载器和父类的父类加载器:
Thread t = Thread.currentThread(); ClassLoader loader = t.getContextClassLoader(); System.out.println(loader); System.out.println(loader.getParent()); System.out.println(loader.getParent().getParent());
打印结果如下:
sun.misc.Launcher$AppClassLoader@5c647e05 sun.misc.Launcher$ExtClassLoader@3d4eac69 null
其中系统类加载器是AppClassLoader的一个实例;
拓展类加载器PlatFormClassLoader的一个实例;
最后的拓展类加载器的父类加载器是根类加载器,但打印出的是null,因为根类加载器没有继承ClassLoader,它是C语言实现的不是Java实现的,所以查看不到。
写在最后:
第一次写技术类博文,希望各路大神多加指正!也欢迎补充!在技术的路上一起成长,共勉之。

浙公网安备 33010602011771号