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

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
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 217,907评论 6 506
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 92,987评论 3 395
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 164,298评论 0 354
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 58,586评论 1 293
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 67,633评论 6 392
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 51,488评论 1 302
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 40,275评论 3 418
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 39,176评论 0 276
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 45,619评论 1 314
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 37,819评论 3 336
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 39,932评论 1 348
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 35,655评论 5 346
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 41,265评论 3 329
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 31,871评论 0 22
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,994评论 1 269
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 48,095评论 3 370
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 44,884评论 2 354

推荐阅读更多精彩内容

  • 齐侯至自田(1),晏子侍于遄台(2),子犹驰而造焉(3)。公曰:“唯据与我和夫!”晏子对曰:“据亦同也,焉得为和?...
    陀思妥耶夫丝姬阅读 5,910评论 0 0
  • 上床之前她到处找她的娃娃,翻箱倒柜。妈妈一边帮她找,一边跟爸爸讨论,小时候不爱玩,现在倒是玩起来了。 她本来应该有...
    Stefinnocence阅读 262评论 0 2
  • 他父親撒迦利亞被聖靈充滿了,就預言說: 被聖靈充滿的人,就說預言,稱頌主。 正如主藉著從創世以來聖先知的口所說的話...
    YNC再寫一篇阅读 81评论 0 0
  • 什么时候想写。 想要表达,想要发泄的时候。表达自己的感动,发泄自己的不满。不管对方是否能听懂,能否能明白,对方是否...
    石不时放松一下阅读 282评论 0 0