范文健康探索娱乐情感热点
投稿投诉
热点动态
科技财经
情感日志
励志美文
娱乐时尚
游戏搞笑
探索旅游
历史星座
健康养生
美丽育儿
范文作文
教案论文
国学影视

从JDK源码级别剖析JVM类加载机制

  main方法的执行流程
  image-20230224180340262
  1:先把文件编译成class文件
  2:window下,java.exe调用jvm.dll创建虚拟机,dll文件相当于jar包。
  3:c++创建引导类加载器。
  4:实例化Launcher,由引导类加载器加载。
  5:加载JvmTest.class文件
  6:执行main方法 loadClass加载流程,类是如何加载到java虚拟机的?static int a = 10; int b = 5;
  1:加载:将字节码文件丢到内存
  2:验证:需要验证字节码格式是否正确。Cafe baba标准的字节码文件开头。
  3:准备:对静态变量进行赋值,赋上默认值,将a赋为0。
  4:解析:将符号引用转变成直接引用,只是将静态的能够确定被谁调用的才会被转变,这个叫做静态解析,类加载时候完成。
  动态解析,在编译的时候没有办法确定被谁调用,只能在运行的时候才能判断出来,比如多态。类运行时候完成。
  符号引用包括三种:类、接口的符号引用。字段的符号引用。方法的符号引用。
  直接引用:符号的内存地址。
  5:初始话:将静态遍变量真正赋值,将a赋值为10,静态代码块也会在这里执行 。
  6:将class文件加载到JVM虚拟机中。 类的加载机制一般是懒加载,在只有用到的时候才会真正的加载。public class JvmTest {     static int b = 10;     static {         System.out.println("jvmTest静态方法");         System.out.println("b的值 = " + b);     }     public static void main(String[] args) {         System.out.println("执行main方法");         new A();     } } class A {     static {         System.out.println("A的静态方法");     }     A() {         System.out.println("A的构造方法");     } } class B {     static {         System.out.println("B的静态方法");     }     B(){         System.out.println("B的构造方法");     } }
  执行结果 : jvmTest静态方法 b的值 = 10 执行main方法 A的静态方法 A的构造方法
  A的静态代码块输出是在main方法之后,也就是在真正使用的时候才会被加载。然后B没有被使用,所以不会被加载。 类加载器和双亲委派机制
  类加载器分为:
  1:引导类加载器:是由c++创建,在jvm创建之后加载,它负责加载JRE下lib目录的jar。
  2:扩展类加载器:负责加载JER下lib的ext,extClassLoader
  3:应用程序加载器:负责加载应用程序,appClassLoader
  4:自定义加载器:自己定义的 System.out.println("String加载类:" + String.class.getClassLoader()); System.out.println("DESKeyFactory加载类:" + com.sun.crypto.provider.DESCipher.class.getClassLoader()); System.out.println("JvmTest加载类:" + JvmTest.class.getClassLoader());
  输出 : String加载类:null  DESKeyFactory加载类:sun.misc.Launcher$ExtClassLoader@372f7a8d JvmTest加载类:sun.misc.Launcher$AppClassLoader@18b4aac2
  String的加载类是引导类加载器加载的,引导类是由c++生成的,所以看不到,是个null。
  extClassLoader是扩展类加载器。
  AppClassLoader引用程序加载器。
  Launcher
  C++引导类加载完之后会实例化Launcher class Launcher{ private ClassLoader loader; private static Launcher launcher = new Launcher(); public static Launcher getLauncher() {      return launcher; }     ... ... }
  launcher是个静态变量,是个单例,在加载的时候就初始话好了。
  初始话的时候实例化ext和app ClassLoader public Launcher() { //实例化扩展类加载器,它的父类没有设置值 Launcher.ExtClassLoader var1 =          Launcher.ExtClassLoader.getExtClassLoader(); //初始化APPclassLoader,并且将ext传入设置为父类加载器     this.loader = Launcher.AppClassLoader.getAppClassLoader(var1); }
  loader赋值为appClassLoader,默认先用appClassLoader加载 双亲委派机制
  image-20230224132150805
  C++在加载的时候调用 Launcher.getLauncher().getClassLoader().loadClass("JvmTest.class")
  Launcher的classLoader是在实例化的时候赋值了AppClassLoader。
  1:AppClassLoader去自己已经加载的类里边找,如果没有向上委托
  2:ExtClassLoader去自己已经加载的类里边找,如果没有向上委托。
  3:引导类去自己加载的类里边找,如果没有,
  4:引导类尝试加载,判断这个类是不是应该由自己加载,判断是不是JRE包路径下的,如果是加载并且返回,不是向下传播
  5:ExtClassLoader判断这个类是不是ext包路径下的,如果是自己加载并返回,不是向下传播
  6:AppClassLoader判断这个类是不是classPath路径下的,如果是的化加载。不是的话,ClassNotFound
  为什么先由AppClassLoader,而不是引导类开始?
  因为大部分类都是我们自己写的,百分之95都是存放到AppClassLoader,只有第一次加载的时候会多走一步,之后大部分都直接从AppClassLoader里边获取。
  loadClass("JvmTest.class")源码 Class<?> loadClass(String name, boolean resolve){     //从自己加载的类里边找     Class<?> c = findLoadedClass(name);   if (c == null) {      if (parent != null) {             //父加载器加载             c = parent.loadClass(name, false);         } else {             //ext的父是null,这是最后一层,引导类类加载器             c = findBootstrapClassOrNull(name);       }       if (c == null) {               //由URLClassLoader实现             c = findClass(name);        }    }     return c; } URLClassLoader.findClass源码Class<?> findClass(final String name){     String path = name.replace(".", "/").concat(".class");     //判断是不是自己要加载的。jrt/  ext/     Resource res = ucp.getResource(path, false);     if (res != null) {         //真正加载class的方法         return defineClass(name, res);     }     if (result == null) {         throw new ClassNotFoundException(name);     }     return result; }
  新增一个String类 package java.lang; public class String {     public static void main(String[] args) {         System.out.println(111);     } }
  执行报错信息: 错误: 在类 java.lang.String 中找不到 main 方法, 请将 main 方法定义为:    public static void main(String[] args) 否则 JavaFX 应用程序类必须扩展javafx.application.Application
  我们新增的 java.lang.String,是能够在引导类加载器的JRE包下边找到的,并且返回String类,这个返回的是jdk自带的类,并不是我们自己写的类,所以会报找不到main方法。
  jdk为什么要用双亲委派机制 ?
  jdk不让修改自己内部的类,沙箱安全机制,防止核心API被篡改。
  避免类的重复加载,父加载器已经加载完了,自己就不用再加载了。
  全盘负责 : public class JvmTest {     static User user; }
  当加载JvmTest类的时候,也会加载User类,这两个类都会由同一个类加载器加载,不会由乱七八糟的加载器加载。 自定义加载器
  我们要自定义加载器,只需要实现ClassLoader,重写findClass就可以。 public class MyClassLoader extends ClassLoader {     private String classPath;      public MyClassLoader(String classPath) {         this.classPath = classPath;     }     private byte[] loadByte(String name) throws Exception {         name = name.replaceAll(".", "/");         FileInputStream fis = new FileInputStream(classPath + "/" + name + ".class");         int len = fis.available();         byte[] data = new byte[len];         fis.read(data);         fis.close();         return data;     }     @Override     protected Class<?> findClass(String name) throws ClassNotFoundException {         try {             byte[] data = loadByte(name);             //真正的加载步骤             return defineClass(name, data, 0, data.length);         } catch (Exception e) {             e.printStackTrace();             throw new ClassNotFoundException();         }     }     public static void main(String[] args) throws Exception {         MyClassLoader myClassLoader =                 new MyClassLoader("C:/myClassLoader");         Class<?> clazz = myClassLoader.loadClass("com.bbk.code.User");         Object o = clazz.newInstance();         Method method = clazz.getDeclaredMethod("eat");         method.invoke(o);         System.out.println("当前MyClassLoader的类加载器:"+MyClassLoader.class.getClassLoader());     } }
  自定义classLoade继承ClassLoader初始话会先初始化父类,在这时候会给自定义classLoader赋值parent为AppClassLoader。具体代码体现在: protected ClassLoader() {         this(checkCreateClassLoader(), getSystemClassLoader());     } private ClassLoader(Void unused, ClassLoader parent) {         this.parent = parent;         ... ... } public static ClassLoader getSystemClassLoader() {         initSystemClassLoader();           ... ...         return scl; } private static synchronized void initSystemClassLoader() {     ... ...     sun.misc.Launcher l = sun.misc.Launcher.getLauncher();     scl = l.getClassLoader(); //返回this.loader,引导类在加载Launcher的时候会赋值为AppClassLoader     ... ... }
  执行main方法,需要把User的class从target中删除,并且加入到我们自定义的文件夹中。执行输出 我是User类eat方法,我的加载器是com.bbk.code.MyClassLoader@5a07e868 如何打破双亲委派机制
  意思就是不在委托父加载,直接自己加载类。双亲委派的逻辑代码实现在loadClass方法中。需要继承ClassLoader重写loadClass方法即可:
  image-20230224173458980
  把红框的代码删除掉。     @Override     protected Class<?> loadClass(String name, boolean resolve)             throws ClassNotFoundException     {         synchronized (getClassLoadingLock(name)) {             // First, check if the class has already been loaded             Class<?> c = findLoadedClass(name);             if (c == null) {                 c = findClass(name);             }             if (resolve) {                 resolveClass(c);             }             return c;         }     }
  重新执行main方法,报错: java.io.FileNotFoundException: C:myClassLoaderjavalangObject.class (系统找不到指定的路径。)
  在加载User的时候,会现在父类Object,我们打破双亲委派之后,自定义的加载器不存在Object所以会报错。
  我们把Object.class放入到我们的本地文件夹中。重新执行报错: //禁止加载java.lang 包 java.lang.SecurityException: Prohibited package name: java.lang
  java.lang必须由我们引导类加载器加载,沙箱安全。我们只能让我们的引导类去加载java.lang。修改代码     @Override     protected Class<?> loadClass(String name, boolean resolve)             throws ClassNotFoundException     {         synchronized (getClassLoadingLock(name)) {             // First, check if the class has already been loaded             Class<?> c = findLoadedClass(name);             if (c == null) {                 if(!name.startsWith("com.bbk")){                     c = this.getParent().loadClass(name);                 }else{                     c = findClass(name);                 }              }             if (resolve) {                 resolveClass(c);             }             return c;         }     }
  只有当name是由com.bbk开头的由我们加载器加载。其他的都由父加载器加载。不会报错。

中国国航无人陪伴机票预定,中国国航无人陪伴机票提前多久申请关注航旅在线了解更多省钱小妙招。相信很多人都有这种经历,孩子需要到外地或者回老家,自己因为工作的原因忙的实在抽不开时间来,所以航空公司推行了无人陪伴服务,可以不需要家属和监护人一同中国十大勾魂景色美到窒息罗平油菜花每年的春节过后,罗平坝子便是油菜花的海洋,荡漾着清香。蓝天映衬着峰林,峰林衬托着花田,千山万壑,花开成海,座座孤锋犹如花海中荡漾的岛屿。漫山遍野的金黄,如江水般轻舒漫卷荡湖北集旅游观光休闲度假于一体的4A级景区,是中国木兰八景之一一个城市的旅游业发展趋势的速度迅速,也是有许多城市针对旅游业的关注水平变的更高一些,并且许多风景区的发展前景非常大,根据与许多园林景观的连续管理方法下看,许多风景区的发展潜力都是会中国篮协名人堂15人出炉,姚明和易建联暂不在内众所周知,最近中国男篮正在墨尔本征战世预赛,而与此同时,中国篮协在近期在国内做出重磅决定,同样引起很多球迷的关注。就在最近中国篮协宣布,接下来将会在近期成立中国篮协名人堂,用来表彰中国男篮锋线有惊喜,广厦男篮小将首秀完美,搭档张镇麟让人期待中国男篮目前正在征战世预赛,在已经结束的两场比赛中,取得了一胜一负的战绩,目前在小组中排名第二,已经提前拿到了晋级下一轮的名额。通过这两场比赛来看,我们中国男篮的整体表现还是非常不国产操作系统大消息国家整合资源,发布开放麒麟,不内卷众所周知,在全球的PC操作系统领域,WindowsmacOS处于绝对主导地位,占了90以上的市场份额。国产操作系统,虽然这些年努力发展,但其所属的linux分类,占比不到5,差得非科贝巴萨有莱万等4大目标若激活第2个杠杆将有2亿欧引援资金直播吧7月2日讯科贝电台称,巴萨今夏引援有4大目标莱万孔德拉菲尼亚B席。巴萨相信,若他们能激活第2个经济杠杆,将有2亿欧的引援资金。此前在与第六街达成部分电视转播权的转让协议后,巴DNF110级BUFF换装价格有多离谱?旭旭宝宝直呼价格上头buff换装作为提升伤害最重要的细节之一,是玩家特别注重打造的对象!换装自86版本就已经有了,不过那时候由于对平民萌新并不公平,不是某些部位太贵,就是绝版了,因此很难拉满属性,例如等额本息VS等额本金,傻傻分不清楚?一分钟让你读懂说到买房,大多数人选择向银行贷款,然而贷款买房一般有两种选择方式等额本息和等额本金。两个词仅一字之差,但却有着很大的不同。那么,我们在买房的时候,到底要选择哪种还款方式,会更加适合怪不得玩家全都跑到韩服,DNF手游韩服这几项做得真得不错爱生活,爱游戏,大家好,我是你们的好朋友汤圆。关注汤圆,收获更多快乐哦!说起DNF手游,国内目前基本上已经凉凉,就连前几天鹅厂的27号游戏发布会,甚至对这个游戏只字未提,这不仅让苦这样的黄金首饰,才能算是艺术品点击关注不迷路,关注吉尔德,想必有所得古往今来,黄金饰品一直备受关注,不同于现代大众黄金首饰的简约时尚,高奢市场和古法工艺的黄金饰品似乎更能称之为艺术品。黄金有价,工艺无价,得益于
罗布泊正在复活,死亡之海碧波荡漾,水从哪里来?罗布泊,曾用名罗布淖。罗布淖是蒙古语的发音,意思是多水汇集之湖。事实也确实如此,在公元前330年前,塔里木河孔雀河车尔臣河疏勒河等等均汇集于此,形成了我国第二大咸水湖,湖水充盈,也日本少女来中国,不是为旅行,而是另有想法大家出远门的原因总是五花八门,有散心的,有涨见识的,也有出于工作的,而对游客来说无非就是前两种,不过也有例外,就比如热衷中国行的日本小姐姐们,来华可不是为了单纯的旅行,似乎她们还有北三县乘船进京指日可待!最快明年6月或有望实现太赞了!明年夏天,有望实现大运河京冀通航!据报道,近日,位于京冀交界处的北运河杨洼船闸正式开建,明年夏天有望实现大运河京冀通航。按照设计方案,杨洼船闸将打造成生态绿舟,成为京津冀生31!世乒赛男双冠军诞生,一路击败国乒4大世界冠军,创24年纪录北京时间11月30日凌晨,休斯顿世乒赛男双冠军出炉,瑞典组合卡尔松法尔克31击败韩国对手林钟勋张禹珍,拿到冠军。这是瑞典乒乓球近24年来的首个世乒赛冠军,上次在世乒赛登顶的瑞典人还40!樊振东夺冠,国乒创4大纪录,听听邓亚萍孙颖莎王曼昱怎么说30日,目前为止,世乒赛各项奖牌得主均已诞生,樊振东横扫世乒赛最大黑马莫雷高德男单夺冠,孙颖莎24不敌王曼昱无缘女单冠军,谈及输球主因,孙颖莎表示战机球处理没有王曼昱好。至此,国乒李湘目测300斤!穿玫红色羽绒服看不见脖子,秀钻戒被小肉手抢镜冬季选择羽绒服确实很正常,但是在面对羽绒服的时候,要避免穿出臃肿感,这样才不会失去时尚看点,如果在穿衣搭配的过程中,上下都是选择宽松的衣品,虽然能够起到遮肉的效果,可是体积感也增大别瞧不上千元机,贵不一定好用,目前这3款手机足够使用了有些人觉得贵手机一定好用,其实不然,比如今年一些较贵的手机搭载旗舰芯片,结果散热做得很差劲,动不动就发烫,还不如买一款千元机来的实在,而且较贵的手机堆料很足,机身也较重,因为贵未必联想原罪竟是因为踢走了一个人,是谁?近日,科技圈最热门的话题当属联想了吧,周末的时候,笔者就被不少身边的朋友咨询了联想怎么了的问题。虽然联想事件还要从司马南的视频说起,但其实在笔者看来,就算没有这些视频作为导火索,联从OPPO手机再用到vivo,机主憋了一肚子话想吐槽,体验差距很明显说到线下销量高的手机品牌,大家一定都绕不开两位,就是oppo和vivo。不论你是在十八线小乡镇,还是在一线大城市,ov的手机专柜一定都不会缺席。那么同样都是主打高颜值的外观,opp12月1日A股策略主力资金持续加仓4大板块!12月将迎开门红?专注A股市场每日实战操作,让2亿散户不迷路!如果你不是长线投资者,想在A股市场中活下来,那么必须明白,周密的布局计划只能有个好的开始,严格的操盘纪律才能结出丰硕的果实!11月最后一神秘私募突击潜伏,四连板新力金融提示风险,这笔重组交易会黄吗?记者吴治邦编辑新力金融(600318。SH)股价三个一字板涨停,令上海通怡投资管理有限公司旗下的两只私募产品获利颇丰。11月29日晚间,新力金融对外披露的股票交易风险提示公告指出,