1.1 类的生命周期
类在内存中完整的生命周期:加载-->使用-->卸载。其中加载过程又分为:装载、链接、初始化三个阶段
1.2 类的加载过程(重点,背也要背下来)
当程序主动使用某个类时,如果该类还未被加载到内存中,系统会通过加载、链接、初始化三个步骤来对该类进行初始化。如果没有意外,JVM将会连续完成这三个步骤,所以有时也把这三个步骤统称为类加载。
类的加载又分为三个阶段:
(1)装载(Loading)
将类的class文件读入内存,并为之创建一个java.lang.Class对象。
就是加载全类名,下面细讲,此过程由类加载器完成
(2)链接(Linking)
①验证Verify:确保加载的类信息符合JVM规范,例如:以cafebabe开头,没有安全方面的问题。
②准备Prepare:正式为类变量(static)分配内存并设置类变量默认初始值
的阶段,这些内存都将在方法区中进行分配。
③解析Resolve:虚拟机常量池内的符号引用(常量名)替换为直接引用(地址)的过程。
(3)初始化(Initialization)
执行类构造器<clinit>()方法
的过程。类构造器<clinit>()方法
是由编译期自动收集类中所有类变量的赋值动作和静态代码块中的语句合并产生的。
(类构造器是构造类信息的,不是构造该类对象的构造器)。 当初始化一个类的时候,如果发现其父类还没有进行初始化,则需要先触发其父类的初始化。虚拟机会保证一个类的<clinit>()方法
在多线程环境中被正确加锁和同步。
2.1 类加载器
将class文件字节码内容加载到内存中,并将这些静态数据转换成方法区的运行时数据结构,然后在堆中生成一个代表这个类的java.lang.Class对象,作为方法区中类数据的访问入口。
类缓存:标准的JavaSE类加载器可以按要求查找类,但一旦某个类被加载到类加载器中,它将维持加载(缓存)一段时间。不过JVM垃圾回收机制可以回收这些Class对象。
2.2类加载器的分类(掌握)
JVM支持两种类型的类加载器,分别为引导类加载器(Bootstrap ClassLoader)
和自定义类加载器(User-Defined ClassLoader)
。
从概念上来讲,自定义类加载器一般指的是程序中由开发人员自定义的一类类加载器,但是Java虚拟机规范却没有这么定义,而是将所有派生于抽象类ClassLoader的类加载器都划分为自定义类加载器。无论类加载器的类型如何划分,在程序中我们最常见的类加载器结构主要是如下情况:
(1)启动类加载器(引导类加载器,Bootstrap ClassLoader)
-
这个类加载使用
C/C++语言
实现的,嵌套在JVM内部。获取它的对象时往往返回null -
它用来加载Java的核心库(JAVA_HOME/jre/lib/rt.jar或sun.boot.class.path路径下的内容)。用于提供JVM自身需要的类。
-
并不继承自java.lang.ClassLoader,没有父加载器。
-
出于安全考虑,Bootstrap启动类加载器只加载包名为java、javax、sun等开头的类
-
加载扩展类和应用程序类加载器,并指定为他们的父类加载器
(2)扩展类加载器(Extension ClassLoader)
-
Java语言编写,由sun.misc.Launcher$ExtClassLoader实现。
-
继承于ClassLoader类
-
父类加载器为启动类加载器
-
从java.ext.dirs系统属性所指定的目录中加载类库,或从JDK的安装目录的jre/lib/ext子目录下 加载类库。如果用户创建的JAR放在此目录下,也会自动由扩展类加载器加载。
(3)应用程序类加载器(系统类加载器,AppClassLoader)
-
java语言编写,由sun.misc.Launcher$AppClassLoader实现
-
继承于ClassLoader类
-
父类加载器为扩展类加载器
-
它负责加载环境变量classpath或系统属性 java.class.path 指定路径下的类库
-
应用程序中的类加载器默认是系统类加载器。
-
它是用户自定义类加载器的默认父加载器
-
通过ClassLoader的getSystemClassLoader()方法可以获取到该类加载器
3.1双亲委派模型高频面试题
双亲委派其实就发生在“加载环节”第一步,算不上“核心”。
主要就是描述JVM中类加载的过程中,找文件的过程中。
看图:这里是JVM中内置的三个类加载器,负责加载不同的类(重点)
引导类加载器,Bootstrap ClassLoader 爷爷
负责加载标准库
扩展类加载器(Extension ClassLoader) 父亲
负责加载扩展目录
应用程序类加载器(系统类加载器,AppClassLoader) 儿子
负责加载第三方的库和你自己写的代码类
注:此处的“父子关系”不是通过类的继承表示的(不是父类,子类),而是通过类加载器
中存在一个“parent”这样的字段指向自己的父亲
类似于二叉树的“三叉实现形式”
所以说双亲委派模型本身说法是不正确的,我认为叫"单亲委派模型更好"
模拟加载流程(重点)
列如,给一个类的全类名,这里我给一个我自己写的类,如Java222.Test
加载过程如下
1 . 工作从ApplicationClassLoader开始进行
ApplicationClassLoader并不会立即搜索第三方相关的目录,而是把任务交给自己的父亲来处理
2 . 工作到了Extension ClassLoader
Extension ClassLoader 也不会立即搜索负责的扩展类目录,也是继续交给自己的父亲来处理
3 工作到了BootstrapClassLoader
BootstrapClassLoader也想交给自己的父亲来处理,可是它的parent=null,所以只能自己来处理,BootstrapClassLoader开始在标准库里面来搜索这个类
此时,如果在如果在 BootstrapClassLoader中查找到了这个类就直接开始读取文件,查找文件等操作,但是如果在BootstrapClassLoader中查找不到Java222.Test这个类,就会还给自己的儿子来处理这个任务,我们继续往下走
4. 此时工作回到了 Extension ClassLoader
Extension ClassLoader开始在自己负责的扩展类里查找,如果查找到了这个类就直接开始读取文件,查找文件等,如果没找到就交给自己的儿子来处理
5. 工作回到了ApplicationClassLoader
此时ApplicationClassLoader在自己负责的第三方目录里查找,查到就开始读取文件,打开文件,但是找不到,这里就没有儿子了,就会抛出一个ClassNotFoundException的异常
总结:这个过程其实一个不难,有点望父成龙的感觉,但是这是应对一下场景,列如,你在你的代码写了一个类,类的名字和标准库/扩展库的名称冲突了。此时JVM就会保证加载标准库的类而不是你的类,如果标准库的类没有被夹在,而是加载了你自己写,那代码不就崩了吗。
标签:委派,初始化,java,Extension,ClassLoader,双亲,JVM,加载 From: https://blog.csdn.net/weixin_75230545/article/details/139279558