微信公众号搜"智元新知"关注
微信扫一扫可直接关注哦!

JVM知识点二--------类加载

JVM知识点二--------类加载

类加载流程

一个Class在虚拟机中的完整生命周期:

加载--验证--准备--解析--初始化--使用--卸载

加载

加载一个Class需要完成以下3件事:

(1)通过Class的全限定名获取Class的二进制字节流

(2)将Class的二进制内容加载到虚拟机的方法

(3)在内存中生成一个java.lang.class对象表示这个Class

获取Class的二进制字节流这个步骤有多种方式:

(1)从zip中读取,如:从jar、war、ear等格式的文件中读取Class文件内容

(2)从网络中获取,如:Applet

(3)动态生成,如:动态代理、ASM框架等都是基于此方式

(4)由其他文件生成,典型的是从jsp文件生成相应的Class

验证

验证一个Class的二进制内容是否合法,主要包括4个阶段:

  • 文件格式验证,确保文件格式符合Class文件格式的规范。如:验证魔数、版本号等。

  • 元数据验证,确保Class的语义描述符合Java的Class规范。如:该Class是否有父类、是否错误继承了final类、是否一个合法的抽象类等。

  • 字节码验证,通过分析数据流和控制流,确保程序语义符合逻辑。如:验证类型转换是合法的。

  • 符号引用验证,发生于符号引用转换为直接引用的时候(转换发生在解析阶段)。如:验证引用的类、成员变量、方法的是否可以被访问(IllegalAccessError),当前类是否存在相应的方法、成员等(NoSuchMethodError、NoSuchFieldError)。

准备

在准备阶段,虚拟机会在方法区中为Class分配内存,并设置static成员变量的初始值为认值。注意这里仅仅会为static变量分配内存(static变量在方法区中),并且初始化static变量的值为其所属类型的认值。如:int类型初始化为0,引用类型初始化为null。即使声明了这样一个static变量:

public static int a = 123;

在准备阶段后,a在内存中的值仍然是0, 赋值123这个操作会在中初始化阶段执行,因此在初始化阶段产生了对应的Class对象之后a的值才是123 。

解析

解析阶段,虚拟机会将常量池中的符号引用替换为直接引用,解析主要针对的是类、接口、方法、成员变量等符号引用。在转换成直接引用后,会触发校验阶段的符号引用验证,验证转换之后的直接引用是否能找到对应的类、方法、成员变量等。这里也可见类加载的各个阶段在实际过程中,可能是交错执行。

初始化

初始化,则是为标记为常量值的字段赋值的过程。换句话说,只对static修饰的变量或语句块进行初始化。

如果初始化一个类的时候,其父类尚未初始化,则优先初始化其父类

如果同时包含多个静态变量和静态代码块,则按照自上而下的顺序依次执行。

类加载器

JVM三种预定义类型加载器

启动类加载器(BootStrap class loader):它用来加载 Java 的核心类,是用原生代码来实现的,并不继承自 java.lang.classLoader(负责加载$JAVA_HOME中jre/lib/rt.jar里所有的class,由C++实现,不是ClassLoader子类)。由于引导类加载器涉及到虚拟机本地实现细节,开发者无法直接获取到启动类加载器的引用,所以不允许直接通过引用进行操作。

扩展类加载器(extensions class loader): 它负责加载JRE的扩展目录,lib/ext或者由java.ext.dirs系统属性指定的目录中的jar包的类。由Java语言实现,父类加载器为null。

系统类加载器system class loader):它根据 Java 应用的类路径(CLAsspATH)来加载 Java 类。一般来说,Java 应用的类都是由它来完成加载的。可以通过 ClassLoader.getSystemClassLoader()来获取它。

类加载机制

全盘负责:当一个类加载器负责加载某个Class时,该Class所依赖和引用其他Class也将由该类加载器负责载入,除非显示使用另外一个类加载器来载入。

缓存机制:缓存机制将会保证所有加载过的Class都会被缓存,当程序中需要使用某个Class时,类加载器先从缓存区中搜寻该Class,只有当缓存区中不存在该Class对象时,系统才会读取该类对应的二进制数据,并将其转换成Class对象,存入缓冲区中

双亲委派机制:双亲委派机制,其工作原理的是,如果一个类加载器收到了类加载请求,它并不会自己先去加载,而是把这个请求委托给父类的加载器去执行,如果父类加载器还存在其父类加载器,则进一步向上委托,依次递归,请求最终将到达顶层的启动类加载器,如果父类加载器可以完成类加载任务,就成功返回,倘若父类加载器无法完成此加载任务,子加载器才会尝试自己去加载,这就是双亲委派模式

双亲委派机制的好处:

  保证java核心库的安全性,防止核心API被随意篡改(例如:如果用户自己写了一个java.lang.String类就会因为双亲委派机制不能被加载,不会破坏原生的String类的加载)

避免重复加载:父类加载了该类,子类没有必要再去加载

代理模式

  与双亲委派机制相反,代理模式是先自己尝试加载,如果无法加载则向上传递。tomcat就是代理模式。

 

 

 

 

 

 

版权声明:本文内容由互联网用户自发贡献,该文观点与技术仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 [email protected] 举报,一经查实,本站将立刻删除。

相关推荐