类的加载过程

编译:把我们写好的java文件,通过javac命令编译成字节码,也就是.class文件
运行:把编译生成的.class文件交给jvm执行
类加载过程:举个通俗点的例子来说,JVM在执行某段代码时,遇到了class A, 然而此时内存中并没有class A的相关信息,于是JVM就会到相应的class文件中去寻找class A的类信息,并加载进内存中,这就是我们所说的类加载过程。
由此可见,JVM不是一开始就把所有的类都加载进内存中,而是只有第一次遇到某个需要运行的类时才会加载,且只加载一次。
类加载分为三步:
  • 加载
  • 链接
  • 初始化
链接分为三步
  • 验证
  • 准备
  • 解析

加载:加载就是把class文件从各个来源通过类加载器加载进内存
这里有两个名字需要解释:
  • 字节码来源:一般的加载来源包括本地路径下编译生成的.class文件、从jar包中的.class文件、远程网络、动态代理实时编译
  • 类加载器:一般包括启动类加载器、扩展类加载器、应用类加载器、以及自定义类加载器
启动类加载器:主要加载的是JVM自身需要的类,C++语言实现的,负责将 <JAVA_HOME>/lib路径下的核心类库或-Xbootclasspath参数指定的路径下的jar包加载到内存中
扩展类加载器:是Launcher的静态内部类,java语言实现的,它负责加载<JAVA_HOME>/lib/ext目录下或者由系统变量-Djava.ext.dir指定位路径中的类库
系统类加载器:它负责加载系统类路径java -classpath-D java.class.path 指定路径下的类库,也就是我们经常用到的classpath路径,开发者可以直接使用系统类加载器,一般情况下该类加载是程序中默认的类加载器。
在java的日常开发中,类的加载几乎是上述三种类加载器相互配合执行的,必要时,我们还可以自定义类加载器。需要注意的是,类的加载是按需加载,也就是说,只有用到了这个类才会进行加载。并且只会加载一次。jvm加载某个类时,用的是双亲委派模式,即把请求交给父类处理。
验证:主要是为了验证加载进来的字节流是否符合虚拟机规范,不会造成安全错误。
  • 对于文件格式的验证:常量中是否有不被支持的常量?文件中是否有不规范?
  • 对于元数据的验证:该类是否继承了被final修饰的类?类中字段是否与父类冲突?是否出现了不合理的重载?
  • 对于字节码的验证:保证类型转换的合理性
  • 对于符号引用的验证:校验符号的引用中的访问性(public、protected、private等)是否可被当前类访问?
元数据:就是修饰数据的数据。添加到字段、方法、类、包上的额外信息。对数据进行说明的数据。例如:注解。
准备:主要为类变量分配内存,并赋予初值。需要注意的是:初值并不是代码中具体写的初始化的值,而是jvm根据不同变量类型赋予的默认值。如:八种基本类型默认值0,引用类型的默认值是null,常量的初始值就是代码设置的值,如final static num = 123;那么该阶段num的初始值就是123。
解析:将常量池内的符号引用替换为直接引用的过程。
符号引用:即一个字符串给出了一些能够唯一性识别一个方法、一个变量、一个类的相关信息。如方法名、变量名、类名
直接引用:可以理解为内存地址
比如调用hello(),方法的地址是123456。hello就是符号引用,123456就是直接引用
初始化:这个阶段主要对类变量的初始化,根据程序员自己设置的变量赋值。
换句话说,只对static修饰的变量或者语句进行初始化
如果初始化一个类的时候,其父类尚未初始化,则优先初始化其父类
如果同时包含多个静态变量和静态代码块,则按照自上而下的顺序依次执行

 参考:https://mp.weixin.qq.com/s?__biz=MzI4NDY5Mjc1Mg==&mid=2247483934&idx=1&sn=41c46eceb2add54b7cde9eeb01412a90&chksm=ebf6da61dc81537721d36aadb5d20613b0449762842f9128753e716ce5fefe2b659d8654c4e8&scene=21#wechat_redirect

 

posted @ 2020-12-15 17:25  James_Harden  阅读(91)  评论(0编辑  收藏  举报