再战jvm-2 虚拟机类加载机制

            虚拟机类加载机制
  • class文件需要被加载到内存中,这个过程就是类加载

  • java虚拟机把描述类的数据从Class文件加载到内存,并对数据进行校验、转换解析和初始化,最终形
    成可以被虚拟机直接使用的java类型,这个过程被称为虚拟机的类加载机制。这个过程是在程序运行
    期间完成的

  • 类的生命周期
    加载、验证、准备、初始化和卸载这五个阶段的顺序是确定的,类型的加载过程必须按照这种顺序按
    部就班的开始,而解析阶段则不一定:它在某些情况下可以在初始化阶段之后再开始,这里是为了支持
    java语言的运行时绑定(也称动态绑定或晚期绑定).(多态的实现)
    在这里插入图片描述

  • 类开始加载的时机:
    <java虚拟机规范>严格规定有且只有六种情况必须立刻对类进行"初始化’(而加载、验证、准备自
    然需要在此之前开始)
    1 . 遇到new、getstatic、putstatic或invokestatic这四条字节码指令是,如果类型没有进行过初始
    化,则需要先触发其初始化阶段,场景
    使用new关键字实例化对象
    读取或者只一个类型的静态字段
    调用一个类的静态方法
    2 . 使用java.lang.reflect包的方法对类进行反射调用的时候,如果没有初始化,则需要先初始化
    3 . 初始化类的时候,如果其父类还没有初始化,则需要先触发其父类的初始化
    4 . 当虚拟机启动时,用户需要指定一个要执行的主类(包含main()方法的那个类),虚拟机会先初始
    化这个主类
    5 . 当使用jdk7新加入的动态语言支持时,如果一个java.lang.invoke.MethodHandle的实例最后解
    析结果为REF_getStatic、REF_putstatic、REF-invokestatic、REF_newInvokeSpecial四种类
    型的方法句柄(还不知道是个啥),并且这个方法句柄对应的类没有进行过初始化,则需要先触发
    其初始化
    书中有两个特殊的例子,很有意思
    1 . 创建一个xxxx类的数组,并不会触发类的static代码块,但是会创建一个
    org.fenixsoft.classloading.SuperClass的一维数组,这个类有虚拟机自动生成,直接继承Object
    2 . 调用一个类的常量,不会初始化这个类,因为在编译期间已经将这个类的常量直接存储到调用类
    的自身常量池中了

  • 类加载的过程{
    加载阶段{
    主要完成以下3件事
    {
    1 . 通过一个类的全限定名获取定义此类的二进制字节流
    2 . 将这个字节流所代表的静态存储结构转化为方法区的运行时数据结构
    3 . 在内存中生成一个代表这个类的java.lang.Class对象,作为方法区(1.8元数据)这个类的各种数据的访问入口
    }
    .class文件加载的方式
    {
    从本地系统中直接加载
    通过网络获取,场景:web Applent
    从zip压缩包中读取,成为之后jar、war格式的基础
    运行时计算生成,使用最多的是:动态代理
    有其他文件生成,典型场景:jsp应用
    从专有数据库中提取.class文件,比较少见
    从加密文件中获取,典型的防class文件被反编译的保护措施
    }
    不同的类被不同的加载器加载,看下面的类加载器
    }
    链接阶段{
    验证{
    确保class文件符合虚拟机的要求,不会危害虚拟机自身安全
    四种验证:文件格式验证、元数据验证、字节码验证、符号引用验证
    }
    准备{
    · 为类变量分配内存,并设置该变量的默认初始值
    · 这里不包含用final修饰的static,用为final在编译的时候就会分配了,准备阶段会显示初始化
    · 这里不会为实例变量分配初始化,类变量会分配在方法区(元数据区)中,而实例变量是会随着对象一起
    分配到java堆中
    }
    解析 / 配合 自己后面笔记中的 动态连接 静态连接 虚方法 非虚方法 常量池 理解{
    · 将常量池的符号引用转换为直接引用的过程
    · 事实上,解析操作往往会伴随着JVM执行完初始化后再执行(上面有讲,为了运行时绑定)
    · 符号引用就是一组符号来描述引用的目标,符号引用的字面量形式明确定义在<java虚拟机规范>的
    Class文件格式中,直接引用就是直接指向目标的指针、相对偏移量或一个间接定位到目标的句柄
    · 解析动作主要针对类或接口、字段、类方法、接口方法、方法类型等,对应常量池中的CONSTANT
    Class info、CONSTANT Fieldref info 、CONSTANT Methodref info等
    }
    }
    初始化{
    · 初始化阶段就是执行类构造器方法()的过程
    clinit是将静态代码块和显示初始化(类变量)合并在一起的方法,此方法不需要定义,是javac编译器
    自动收集类中所有类变量的赋值动作和静态代码块中的语句合并而来
    · 构造器方法中指令按语句在源文件中出现的顺序执行
    · ()不同于类的构造器(不同于init() 就是那个构造器…)
    · 若该类具有父类,jvm会保证子类的()执行前,父类的()已经执行完毕
    · 虚拟机必须保证一个类的()方法在多线程下被同步加锁
    }
    }

  • 类加载的分类{
    注意:这里是上下级关系,不是java中的继承关系
    在这里插入图片描述

这里是java中的继承关系图,这些所属的自定义类加载器,在sun.misc,Launcher中可以找到
在这里插入图片描述

代码演示,不同的类加载器

//系统类加载器
ClassLoader systemClassLoader = ClassLoader.getSystemClassLoader();
System.out.println(systemClassLoader);
//扩展类加载器  1.9之后被平台类加载器(Platform ClassLoader)取代
ClassLoader extClassLoader = systemClassLoader.getParent();
System.out.println(extClassLoader);
//引导类加载器,null,C和C++实现的无法获取 BootStrapClassLoader
ClassLoader bootstrapClassLoader = extClassLoader.getParent();
System.out.println(bootstrapClassLoader);
//系统类加载器
ClassLoader classLoader = MyJvmTest.class.getClassLoader();
System.out.println(classLoader);
//引导类加载器,null,C和C++实现的无法获取,用来加载java核心类
ClassLoader classLoader2 = String.class.getClassLoader();
System.out.println(classLoader2); 

引导类加载器{
Bootstrap ClassLoader C和C++实现的,其他加载器都是java语言本身实现的
用来加载JAVA的核心库(JAVA_HOME/jre/lib/rt.jar、resource.jar或sun,boot.class.path路径下的内容),用于提供JVM自身需要的类
并不继承自java.lang.ClassLoader,没有父加载器
加载扩展类和应用类程序加载器,并指定为他们的父类加载器
处于安全考虑,Bootstrap启动类加载器只加载包名为java、javax、sun等开头的类
}
自定义加载器{
User-Defined ClassLoader
java虚拟机规范定义:所有派生于抽象类ClassLoader的类加载器都划分为自定义类加载器
系统类加载器(AppClassLoader),和上面的第一张图有所不同
java语言编写,由sun,misc.Launcher$AppClassLoader实现
派生于 ClassLoader类
父类加载器为扩展类加载器
负责加载环境变量classpath或系统属性 java.class.path 指定路径下的类库
是程序中默认的类加载器,一般来说,java应用的类都是由它来完成加载
通过ClassLoader#getSystemClassLoader()方法可以获取到该类加载器
我们可以自定义类加载器,来指定类的加载方式,有什么用途
隔离加载类
修改类加载方式
扩展加载源
防止源码泄露
}
}

  • 一个类型必须与加载器一起确定唯一性
    代码举例,不同的,不同的类加载器对instance关键字运算结果的影响
public static void main(String[] args) throws Exception {
    ClassLoader myClassLoader = new ClassLoader() {
        @Override
        public Class<?> loadClass(String name) throws ClassNotFoundException {

            try {
                String fileName = name.substring(name.lastIndexOf(".") + 1) + ".class";
                InputStream is = getClass().getResourceAsStream(fileName);
                if (is == null) {
                    return super.loadClass(name);
                }
                byte[] b = new byte[is.available()];
                is.read(b);
                return defineClass(name, b, 0, b.length);
            } catch (IOException e) {
                throw new ClassNotFoundException(name);
            }

        }
    };

    Object myJvmTest = myClassLoader.loadClass("com.example.jvmtest.MyJvmTest").newInstance();
    System.out.println(myJvmTest.getClass());
    System.out.println(myJvmTest instanceof com.example.jvmtest.MyJvmTest);
}
  • 双亲委派模型{
    简单点说就是
    · 类的加载会首先让上级类加载器加载,一直到最上级,如不匹配再从上往下排,先排到谁谁加载
    · 保护了核心代码的安全
    }

java模块化系统 明天再看
模块化下的类加载器 明天再看

参考<深入理解java虚拟机> <java虚拟机详解>的视频>
基本是照着<深入理解java虚拟机>来的,仅供自己复习参考

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
JVM中的双亲委派机制是一种类加载机制,它规定了在Java中一个类被加载时如何进行类加载器的选择。根据这个机制,当一个类需要被加载时,首先会由类加载器ClassLoader检查是否已经加载过该类,如果是,则直接返回已经加载过的类;如果不是,则将该请求委派给父类加载器去加载。这样的过程会一直向上委派,直到达到顶层的引导类加载器(Bootstrap ClassLoader)。引用 引用中提到,并不是所有的类加载器都采用双亲委派机制。Java虚拟机规范并没有强制要求使用双亲委派机制,只是建议使用。实际上,一些类加载器可能会采用不同的加载顺序,例如Tomcat服务器类加载器就是采用代理模式,首先尝试自己去加载某个类,如果找不到再代理给父类加载器。 引用中提到,引导类加载器(Bootstrap ClassLoader)是最早开始工作的类加载器,负责加载JVM的核心类库,例如java.lang.*包中的类。这些类在JVM启动时就已经被加载到内存中。 综上所述,JVM的双亲委派机制是一种类加载机制,它通过类加载器的委派方式来加载类,首先检查是否已经加载过该类,如果没有则委派给父类加载器去加载,直到达到顶层的引导类加载器。不过,并不是所有的类加载器都采用该机制,一些类加载器可能会采用不同的加载顺序。引用<span class="em">1</span><span class="em">2</span><span class="em">3</span> #### 引用[.reference_title] - *1* [JVM-双亲委派机制](https://blog.csdn.net/m0_51608444/article/details/125835862)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v93^chatsearchT3_2"}}] [.reference_item style="max-width: 50%"] - *2* *3* [jvm-双亲委派机制](https://blog.csdn.net/y08144013/article/details/130724858)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v93^chatsearchT3_2"}}] [.reference_item style="max-width: 50%"] [ .reference_list ]

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值