JVM类加载机制

类的生命周期:
加载、连接(验证、准备、解析)、初始化、使用、卸载。解析阶段不一定按顺序,它可以在初始化阶段后开始。

类加载的过程:
  1. 加载:根据查找路径找到相应的class文件然后导入
  2. 验证:检查加载的class文件的正确性
  3. 准备:给类中的静态变量分配内存空间
  4. 解析:虚拟机常量池内的符号引用替换为直接引用(地址引用)
  5. 初始化:对静态变量和静态代码块进行初始化
初始化顺序:
  1. 父类静态成员和静态块、子类静态成员和静态块
  2. 父类实例变量和非静态块,父类构造函数
  3. 子类实例变量和非静态块,子类构造函数
类的初始化触发情况:
  • new对象
  • 访问某个类或接口的静态变量,或者对该静态变量赋值
  • 调用类的静态方法
  • 反射,Class.forName
  • 初始化子类,其父类也会被初始化
  • 虚拟机启动时被标明为启动的类
类加载器:对任意一个类,都需要由加载它的类加载器和类本身一同确立在JVM中的唯一性。每个类加载器都有独立的类名称空间。类加载器根据指定全限定名将class文件加载到JVM内存,然后转化为class对象
分类:
  • 启动类加载器(Bootstrap ClassLoader),加载JAVA安装目录下lib目录中的核心类库,或者被-Xbootclasspah参数锁指定路径中并且被jvm识别的类库
  • 扩展类加载器(Ext ClassLoader),加载JAVA安装目录下\lib\ext目录或Java.ext.dirs系统变量指定路径中的所有类库
  • 应用程序类加载器(Application ClassLoader),加载ClassPath环境变量指定的路径中的类,可以直接使用该类加载器,默认使用
  • 自定义类加载器,根据需求自定义加载
类加载机制:
  • 全盘负责,负责加载某个Class时,该Class所依赖和引用的其他Class也该由此类加载器载入,除非显式使用另一个来载入
  • 父类委托,先让父类加载器试图加载该类,无法加载时才尝试从自己的类路径中加载类
  • 缓存机制,所有加载过的class会被缓存,程序需要某个class时,类加载器先从缓存区寻找,缓存不存在,系统才会读取该类对应的二进制数据,并转换为class对象存入缓存区。这就是为什么修改了Class后,必须重启JVM,程序的修改才会生效
类加载方式:
  • 命令行启动应用时JVM初始化加载
  • Class.forName加载
  • ClassLoader.loadClass加载
双亲委派:
  • 如果一个类加载器收到了类加载的请求,它首先不会自己去尝试加载这个类,而是把请求委托给父加载器去完成,依次向上。所有的类加载请求都会被传递到启动类加载器中,只有当父加载器在它的搜索范围中没有找到所需的类时,即无法完成该加载,子加载器才会尝试自己去加载该类。
  • AppClassLoader加载一个class,会先委派给ExtClassLoader,然后ExtClassLoader会委派给启动类加载器,如果启动类加载器加载失败会使用ExtClassLoader去加载,加载失败会用AppClassLoader加载,如果还加载失败就抛出异常ClassNoFoundException。
  • 自定义类加载器:
  • 当网络传输加密后的java类的字节码,我们需要自定义类加载器去取解码。继承ClassLoader类并重写findClass方法。最好不要重写loadClass方法,容易破坏双亲委派模式。
  • 为何类加载要父类委托:
  • 每个层级的类加载器各司其职,而且不会重复加载一个类。如果两个不同层级的累加器都去尝试加载某个类,没有双亲委派两个类加载器就可以加载同一个类,造成类的重复加载。
 
posted @ 2025-04-16 17:01  难得  阅读(6)  评论(0)    收藏  举报