JVM角度认识类加载

先整体认识一个java类是怎么从编写到加载进jvm内存的,如下图所示:

认知类加载 - 图1
字节码文件是一个Java官方严格规定字节数、格式的二进制文件,每一个字节都具有特殊的含义,比如前面4个字节:CA FE BA BE表示魔值,魔值代表它是否为一个Class文件、00 00代表Java的此版本号、00 34代表主版本号,这里就不一一介绍了,暂时只需要指定它的每个字节都有着特殊的含义即可。字节码文件存储的一个类的所有信息:版本、属性、方法……
字节码文件的结构详见《深入理解Java虚拟机》的第6章 类文件结构
认知类加载 - 图2
我们首先是先编写“.java”文件,然后这个文件需要通过编译器编译成字节码文件“.class”,然后字节码文件通过类加载器加载进jvm的内存区域(方法区),这样就实现了整体上类加载的大致过程。而类加载过程就是怎么将字节码文件加载进jvm的过程。

类加载总体过程

认知类加载 - 图3
下图是类加载过程的整体作用图:
image.png
一个Java文件从编码到最后执行完成一般会分为编译和运行两个阶段,其中编译指的是通过javac命令将Java文件编译成字节码文件(即 .class文件),而运行指的是将字节码文件交给JVM执行的过程。而类加载就是JVM把 .class文件中类的信息加载进内存(方法区)并进行解析生成对象的class对象(存放堆中)的过程。例如:JVM在执行某段代码的时候,遇到了class Car,JVM就会取内存中取class Car,如果内存中没有的话,JVM就回去对应的class文件中寻找class Car的信息,并将其加载进内存中,内存中有了Car类的信息,就可以进行实例化,如图所示:
认知类加载 - 图5
其中ClassLoader就是Car类的类加载器,用于加载Car类。

类型信息和对象分别存在方法区和堆中:

注意: 上面的几个阶段是按顺序开始,而不是按顺序进行完成,因为这些阶段通常都是互相交叉地混合进行的,通常在一个阶段执行的过程中调用或激活另一个阶段。

JVM类加载的特点

  • 全盘负责:当一个类加载器负责加载某个Class时,该Class所依赖的和引用的其他Class也将由该类加载器负责载入,除非显示使用另外一个类加载器来载入
  • 父类委托:先让父类加载器试图加载该类,只有在父类加载器无法加载该类时才尝试从自己的类路径中加载该类,即后面地双亲委派机制
  • 缓存机制:缓存机制将会保证所有加载过的Class都会被缓存,当程序中需要使用某个Class时,类加载器先从缓存区寻找该Class,只有缓存区不存在,系统才会读取该类对应的二进制数据,并将其转换成Class对象,存入缓存区。这就是为什么修改了Class后,必须重启JVM,程序的修改才会生效