首页 > 编程知识 正文

java多态,JVM类加载过程

时间:2023-05-06 05:09:09 阅读:32602 作者:3753

手把手代码:三小时内springboot—企业级微博项目实战---csdn学院使用程序积极的类时,如果该类尚未加载到内存中,JVM将加载、连接、连接如果没有发生事故,JVM将连续完成三个步骤,因此这三个步骤有时也称为类加载或类初始化。

另一方面,类的加载过程1.加载加载是指将类的class文件读取到存储器中并为其创建java.lang.Class对象。 也就是说,在程序中使用类时,系统将为此创建java.lang.Class对象。

类加载由类加载器进行。 类加载器通常由JVM提供,是运行所有先前程序的基础。 由JVM提供的这些类加载器通常称为系统类加载器。 另外,开发人员可以通过继承ClassLoader基类来创建自己的类加载器。

可以使用不同的类加载器从不同的源加载类的二进制数据。 通常有以下几种来源:

从本地文件系统加载class文件。 这是前面大多数示例程序的类加载方法。 从JAR包加载class文件的方法也很常见。 上述JDBC编程中使用的数据库驱动程序类位于JAR文件中,JVM可以直接从JAR文件加载class文件。 通过网络加载class文件。 动态编译Java源文件并执行加载。 通常,类加载器不需要将类加载到“首先使用”之前。 javavirtualmachinespecification允许预加载特定类。

2 .加载链接类后,系统将生成相应的Class对象,然后进入将类的二进制数据合并到JRE中的连接阶段。 类连接分为以下三个阶段。

1)验证:验证阶段用于验证加载的类是否具有正确的内部结构并与其他类协调。 Java是对c语言安全的语言。 例如,有c不具有的数组越界的检查。 这本身就是保护自己的安全。 验证阶段是Java的一个非常重要的阶段,是直接保证APP应用程序是否被恶意入侵的重要防线,验证机制越严密越安全。 验证的目的是确保Class文件字节流中包含的信息满足当前虚拟机的要求,并且不会损害虚拟机本身的安全。 主要包括4种验证、文件格式验证、元数据验证、字节码验证、符号引用验证。

下面对4个验证进行进一步说明

文件格式验证:验证主要字节流是否符合Class文件格式规范,并且可以加载到当前虚拟机中进行处理。 例如,主、次版本号是否在当前虚拟机的处理范围内。 常量池中是否存在不支持的常量类型。 常量的索引值中是否存在不存在的常量或与类型不匹配的常量。

从语义上分析用 元数据验证:字节码编写的信息,分析是否符合java语言语法规范。

字节码验证:分析最重要的验证环节、数据流和控件,确定含义合法、逻辑。 的主要元数据验证后方法主体的验证。 保证类方法在运行时不会造成危害。

符号引用验证:主要针对符号参照转换为直接参照时,扩展到第三个分析阶段,主要确定访问类型等参照所涉及的情况。 主要是必须访问引用,以避免出现类等无法访问的问题。

2 )准备:类准备阶段为类的静态变量分配内存并设置默认初始值。

3 )将解析:类的二进制数据中的符号引用直接替换为引用。 说明:符号参考:符号参考是用一系列符号描述参考对象,符号可以是任何字面形式的字面量,只要不发生冲突就可以确定位置。 布局与内存无关。 直接引用:可以指向目标的指针、偏移或直接定位的句柄。 此引用与内存中的布局有关,并且一定会加载。

3 .初始化初始化是指为类的静态变量提供正确的初始值。 准备阶段和初始化阶段看起来有点矛盾,但其实并不矛盾。 如果类具有语句:私有静态int a=10,则执行过程如下: 首先将字节码文件加载到内存中,然后首先执行验证链接这一步骤,在验证通过后,在准备阶段将内存分配给a。 由于变量a是静态的,所以此时,a成为int型的默认的初始值0,即a=0,接着到达分析(后述),到达初始化这样的步骤时,第一次将a的真值10赋予a,此时,a=10

二、在加载类的定时创建类的实例。 这意味着静态方法调用反射(class.forname ) com.lyj.load ) )访问new对象所在的类或接口,或将类分配给该静态变量

style="margin-left:0cm;">     除此之外,下面几种情形需要特别指出:

     对于一个final类型的静态变量,如果该变量的值在编译时就可以确定下来,那么这个变量相当于“宏变量”。Java编译器会在编译时直接把这个变量出现的地方替换成它的值,因此即使程序使用该静态变量,也不会导致该类的初始化。反之,如果final类型的静态Field的值不能在编译时确定下来,则必须等到运行时才可以确定该变量的值,如果通过该类来访问它的静态变量,则会导致该类被初始化。

三、类加载器

    类加载器负责加载所有的类,其为所有被载入内存中的类生成一个java.lang.Class实例对象。一旦一个类被加载如JVM中,同一个类就不会被再次载入了。正如一个对象有一个唯一的标识一样,一个载入JVM的类也有一个唯一的标识。在Java中,一个类用其全限定类名(包括包名和类名)作为标识;但在JVM中,一个类用其全限定类名和其类加载器作为其唯一标识。例如,如果在pg的包中有一个名为Person的类,被类加载器ClassLoader的实例kl负责加载,则该Person类对应的Class对象在JVM中表示为(Person.pg.kl)。这意味着两个类加载器加载的同名类:(Person.pg.kl)和(Person.pg.kl2)是不同的、它们所加载的类也是完全不同、互不兼容的。

   JVM预定义有三种类加载器,当一个 JVM启动的时候,Java开始使用如下三种类加载器:

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

下面程序可以获得根类加载器所加载的核心类库,并会看到本机安装的Java环境变量指定的jdk中提供的核心jar包路径:

public class ClassLoaderTest {public static void main(String[] args) {URL[] urls = sun.misc.Launcher.getBootstrapClassPath().getURLs();for(URL url : urls){System.out.println(url.toExternalForm());}}}

运行结果:

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

  3)系统类加载器(system class loader):被称为系统(也称为应用)类加载器,它负责在JVM启动时加载来自Java命令的-classpath选项、java.class.path系统属性,或者CLASSPATH换将变量所指定的JAR包和类路径。程序可以通过ClassLoader的静态方法getSystemClassLoader()来获取系统类加载器。如果没有特别指定,则用户自定义的类加载器都以此类加载器作为父加载器。由Java语言实现,父类加载器为ExtClassLoader。

类加载器加载Class大致要经过如下8个步骤:

检测此Class是否载入过,即在缓冲区中是否有此Class,如果有直接进入第8步,否则进入第2步。如果没有父类加载器,则要么Parent是根类加载器,要么本身就是根类加载器,则跳到第4步,如果父类加载器存在,则进入第3步。请求使用父类加载器去载入目标类,如果载入成功则跳至第8步,否则接着执行第5步。请求使用根类加载器去载入目标类,如果载入成功则跳至第8步,否则跳至第7步。当前类加载器尝试寻找Class文件,如果找到则执行第6步,如果找不到则执行第7步。从文件中载入Class,成功后跳至第8步。抛出ClassNotFountException异常。返回对应的java.lang.Class对象。四、类加载机制:

1.JVM的类加载机制主要有如下3种。

全盘负责:所谓全盘负责,就是当一个类加载器负责加载某个Class时,该Class所依赖和引用其他Class也将由该类加载器负责载入,除非显示使用另外一个类加载器来载入。双亲委派:所谓的双亲委派,则是先让父类加载器试图加载该Class,只有在父类加载器无法加载该类时才尝试从自己的类路径中加载该类。通俗的讲,就是某个特定的类加载器在接到加载类的请求时,首先将加载任务委托给父加载器,依次递归,如果父加载器可以完成类加载任务,就成功返回;只有父加载器无法完成此加载任务时,才自己去加载。缓存机制。缓存机制将会保证所有加载过的Class都会被缓存,当程序中需要使用某个Class时,类加载器先从缓存区中搜寻该Class,只有当缓存区中不存在该Class对象时,系统才会读取该类对应的二进制数据,并将其转换成Class对象,存入缓冲区中。这就是为很么修改了Class后,必须重新启动JVM,程序所做的修改才会生效的原因。

2.这里说明一下双亲委派机制:

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

      双亲委派机制的优势:采用双亲委派模式的是好处是Java类随着它的类加载器一起具备了一种带有优先级的层次关系,通过这种层级关可以避免类的重复加载,当父亲已经加载了该类时,就没有必要子ClassLoader再加载一次。其次是考虑到安全因素,java核心api中定义类型不会被随意替换,假设通过网络传递一个名为java.lang.Integer的类,通过双亲委托模式传递到启动类加载器,而启动类加载器在核心Java API发现这个名字的类,发现该类已被加载,并不会重新加载网络传递的过来的java.lang.Integer,而直接返回已加载过的Integer.class,这样便可以防止核心API库被随意篡改。

版权声明:该文观点仅代表作者本人。处理文章:请发送邮件至 三1五14八八95#扣扣.com 举报,一经查实,本站将立刻删除。