美文网首页
深入JVM内核原理-3.JVM的类加载

深入JVM内核原理-3.JVM的类加载

作者: 笨鸡 | 来源:发表于2019-07-23 00:54 被阅读0次

1.类加载概要

类加载概要.png

2.class加载验证流程

  • 加载
    • 加载类的第一个阶段
    • 取得类的二进制流
    • 转为方法区数据结构
    • 在Java堆中生成对应的java.lang.Class对象
  • 链接
    • 验证
      • 目的:保证Class流的格式是正确的
        • 文件格式的验证
          • 是否以0xCAFEBABE开头
          • 版本号是否合理
        • 元数据验证
          • 是否有父类
          • 继承了final类?
          • 非抽象类实现了所有的抽象方法
        • 字节码验证(很复杂)
          • 运行检查
          • 栈数据类型和操作码数据参数吻合
          • 跳转指令指定到合理的位置
        • 符号引用验证
          • 常量池中描述类是否存在
          • 访问的方法或字段是否存在且有足够的权限
    • 准备
      • 分配内存,并为类设置初始值(方法区中)
        • public static int v = 1;
        • 在准备阶段中,v会被设置为0;
        • 在初始化的<clinit>中才会被设置为1;
        • 对于static final 类型,在准备阶段就会被附上正确的值;
        • public static final int v = 1;
    • 解析
类加载-解析.png
  • 初始化
    • 执行类构造器<clinit>

      • static变量 赋值语句
      • static{} 语句
    • 子类的<clinit>调用前保证父类的<clinit>被调用

    • <clinit>是线程安全的

    • 初始化顺序

      类加载初始化顺序1.png
      package classloader;
      
      public class InitTest {
      
          // 静态变量
          public static String staticField = "静态变量";
      
          // 变量
          public String field = "变量";
      
          // 静态初始化块
          static {
              System.out.println(staticField);
              System.out.println("静态初始化块");
          }
      
          // 初始化块
          {
              System.out.println(field);
              System.out.println("初始化块");
          }
      
          // 构造器
          public InitTest() {
              System.out.println("构造器");
          }
      
          public static void main(String[] args) {
              new InitTest();
          }
      
      }
      

      输出结果:
      静态变量
      静态初始化块
      变量
      初始化块
      构造器

      类加载初始化顺序2.png
      package classloader;
      
      class Parent {
          // 静态变量
          public static String p_StaticField = "父类--静态变量";
          protected int i = 1;
          protected int j = 8;
          // 变量
          public String p_Field = "父类--变量";
      
          // 静态初始化块
          static {
              System.out.println(p_StaticField);
              System.out.println("父类--静态初始化块");
          }
      
          // 初始化块
          {
              System.out.println(p_Field);
              System.out.println("父类--初始化块");
          }
      
          // 构造器
          public Parent() {
              System.out.println("父类--构造器");
              System.out.println("i=" + i + ", j=" + j);
              j = 9;
          }
      }
      
      public class SubClass extends Parent {
      
          // 静态变量
          public static String s_StaticField = "子类--静态变量";
      
          // 变量
          public String s_Field = "子类--变量";
      
          // 静态初始化块
          static {
              System.out.println(s_StaticField);
              System.out.println("子类--静态初始化块");
          }
          // 初始化块
          {
              System.out.println(s_Field);
              System.out.println("子类--初始化块");
          }
      
          // 构造器
          public SubClass() {
              System.out.println("子类--构造器");
              System.out.println("i=" + i + ",j=" + j);
          }
      
          // 程序入口
          public static void main(String[] args) {
              new SubClass();
          }
      }
      

      输出结果:
      父类--静态变量
      父类--静态初始化块
      子类--静态变量
      子类--静态初始化块
      父类--变量
      父类--初始化块
      父类--构造器
      i=1, j=8
      子类--变量
      子类--初始化块
      子类--构造器
      i=1,j=9

3.什么是类加载器ClassLoader

  • ClassLoader是一个抽象类
  • ClassLoader的实例将读入Java字节码将类加载到JVM中
  • ClassLoader可以定制,满足不同的字节码流获取方式
  • ClassLoader负责类加载过程中的加载阶段

3.JDK中ClassLoader默认设计模式

  • ClassLoader的重要方法
    • public Class<?> loadClass(String name) throws ClassNotFoundException
      • 载入并返回一个Class
    • protected final Class<?> defineClass(byte[] b, int off, int len)
      • 定义一个类,不公开调用
    • protected Class<?> findClass(String name) throws ClassNotFoundException
      • loadClass回调该方法,自定义ClassLoader的推荐做法
    • protected final Class<?> findLoadedClass(String name)
      • 寻找已经加载的类
  • 分类
    • BootStrap ClassLoader(启动ClassLoader)
    • Extension ClassLoader(扩展ClassLoader)
    • App ClassLoader(应用ClassLoader/系统ClassLoader)
    • Custom ClassLoader(自定义ClassLoader)
    • 每个ClassLoader都有一个Parent作为父亲
  • 协同工作
协同工作.png
loadClass.png
类加载例子.png
  • 直接运行以上代码:
    • I am in apploader
  • 加上参数 -Xbootclasspath/a: D:<path>
    • I am in bootloader
    • 此时AppLoader中不会加载HelloLoader
      • I am in apploader在classpath中却没有加载
      • 说明类加载是从上往下的
强制AppLoader.png
  • 问题
类加载问题.png
  • 解决
    • Thread.setContextClassLoader()
      • 上下文加载器
      • 是一个角色
      • 用以解决顶层ClassLoader无法访问底层ClassLoader的类的问题
      • 基本思想是,在顶层ClassLoader中,传入底层ClassLoader的实例
类加载例.png
  • 双亲模式的破坏
    • 双亲模式是默认的模式,但不是必须这么做
    • Tomcat的WebappClassLoader就会先加载自己的Class,找不到再委托parent
    • OSGI的ClassLoader形成网状结构,根据需要自由加载Class
破坏双亲模式例子1.png
findClass.png
破坏双亲模式例子2.png
AppLoader结果.png

5.热替换

热替换1.png
热替换2.png

相关文章

网友评论

      本文标题:深入JVM内核原理-3.JVM的类加载

      本文链接:https://www.haomeiwen.com/subject/jjjglctx.html