jvm系列之类加载

骑猪看日落 2022-05-28 07:00 274阅读 0赞

1、前言

ava语言的类型可以分为两大类:基本类型和引用类型。

基本类型 包括byte,short,int,long,float,double,boolean,char。

引用类型 包括类,接口,数组类和泛型参数。由于泛型参数会在编译过程中被擦除,因此Java虚拟机实际上只有前三种。在类,接口和数组类中,数组类是由Java虚拟机直接生成的,其它两种则有对应的字节流

2、ClassLoader类

java.lang.ClassLoader类就是类加载器,是一个抽象类。如果开发人员要编写自己的类加载器,就必须直接或间接地继承这个类,java虚拟机在类加载时机会调用defineClass方法加载字节码文件进入jvm内存中,并创建对应的Class对象。

3、类加载过程

20180526194147827

format_png

3.1、加载

20180526195451884

1.加载,就是查找字节流,并且据此生成一个代表这个类的java.lang.Class对象的过程。注意这里字节流不一定非得要从一个Class文件获取,这里既可以从ZIP包中读取(比如从jar包和war包中读取),也可以在运行时计算生成(动态代理),也可以由其它文件生成(比如将JSP文件转换成对应的Class类)。加载的信息存储在JVM的方法区。

2.对于数组类来说,它并没有对应的字节流,而是由Java虚拟机直接生成的。对于其它的类来说,Java虚拟机则需要借助类加载器来完成查找字节流的过程。

3.类加载器有两种,一种是启动类加载器,其它的类加载器都是java.lang.ClassLoader的子类。启动类加载器是由C++实现的,没有对应的Java对象,因此在Java中只能用null代替。除了启动类加载器之外,另外两个重要的类加载器是扩展类加载器和应用类加载器,均由Java核心类库提供。 启动类加载器加载最为基础,最为重要的类,如JRE的lib目录下jar包中的类;扩展类加载器的父类是启动类加载器,它负责加载相对次要,但又通用的类,如JRE的lib/ext目录下jar包中的类;应用类加载器的父类加载器则是扩展类加载器,它负责加载应用程序路径下的类。

4.JVM通过双亲委派模型进行类的加载,当然我们也可以通过继承java.lang.ClassLoader实现自定义的类加载器。当一个类加载器收到类加载任务,会先交给其父类加载器去完成,因此最终加载任务都会传递到顶层的启动类加载器,只有当父类加载器无法完成加载任务时,才会尝试执行加载任务。

采用双亲委派的好处:

a)是比如加载位于rt.jar包中的类java.lang.Object,不管是哪个加载器加载这个类,最终都是委托给顶层的启动类加载器进行加载,这样就保证了使用不同的类加载器最终得到的都是同样一个Object对象

b)可以避免重复加载,父类已经加载了,子类就不需要再次加载

c)更加安全,很好的解决了各个类加载器的基础类的统一问题,如果不使用该种方式,那么用户可以随意定义类加载器来加载核心api,会带来相关隐患

3.1.1、验证双亲委派

  1. ClassLoader cla = Thread.currentThread().getContextClassLoader();
  2. ClassLoader clb = cla.getParent();
  3. ClassLoader clc = cla.getParent().getParent();
  4. System.out.println(cla);
  5. System.out.println(clb);
  6. System.out.println(clc);

以上程序输出如下:

sun.misc.Launcher$AppClassLoader@18b4aac2
sun.misc.Launcher$ExtClassLoader@1fb3ebeb
null

3.1.2、自定义类加载器

两种方式:

a)遵守双亲委派模型:继承ClassLoader,重写findClass()方法。

b)破坏双亲委派模型:继承ClassLoader,重写loadClass()方法

3.2、连接

3.2.1、验证

目的在于确保class文件的字节流中包含信息符合当前虚拟机要求,不会危害虚拟机自身安全。

3.2.2、准备

为静态域分配空间。如static int i=5,只将i初始化为0,而5在初始化阶段才赋值。这里不包括用final修饰的static,如

final static int A=5,编译期常量不用初始化就可以被读取,而形如final static int A=new Random().nextInt()不是编译期常量,使用时会强制发生初始化。

3.2.3、解析

主要将常量池中的符号引用替换为直接引用。符号引用就是用一组符号来描述目标,而直接引用就是指向目标的指针、相对偏移量或y一个间接定位到目标的句柄。

3.3、初始化

初始化静态域。注意,类加载不会为实例变量分配空间和初始化。

4、class文件的显式加载与隐式加载

4.1、显式加载

如Class.forName()、this.getClass().getClassLoader().loadClass()这种显式地去加载某个类,即为显式加载。

4.2、隐式加载

如在加载某个类的class文件时,该类的class文件中引用了另外一个类的对象,此时这个类就会被隐式地加载到jvm内存当中。

发表评论

表情:
评论列表 (有 0 条评论,274人围观)

还没有评论,来说两句吧...

相关阅读

    相关 JVM之类过程

    当我们在Java代码中写下new String()的时候,我们理所当然认为java会返回给我们一个String对象,但是在JVM背后做了很多事情,包括类的加载、对象内存的分配等

    相关 JVM 之 类

    1.类加载 虚拟机把描述类的数据从class文件加载到内存,并对数据进行校验,解析和初始化, 最终形成可以被虚拟机直接使用的java类型。 2.加载机

    相关 JVM之类

    类从被加载到虚拟机内存中开始,到卸载出内存为止,它的整个生命周期包括:加载、验证、准备、解析、初始化、使用和卸载七个阶段。它们开始的顺序如下图所示: ![classonloa