`

jvm中类加载

    博客分类:
  • JVM
 
阅读更多

一、

首先,java源文件->java编译机->Class文件->ClassLoader类加载器->类加载

其中类加载又分为:加载->验证->准备->解析->初始化->使用->卸载

二、研究类加载机制的意义

从上图可以看出,类加载是Java程序运行的第一步,研究类的加载有助于了解JVM执行过程,并指导开发者采取更有效的措施配合程序执行。

研究类加载机制的第二个目的是让程序能动态的控制类加载,比如热部署等,提高程序的灵活性和适应性。
 
三、类加载的一般过程
原理:双亲委托模式
1、寻找jre目录,寻找jvm.dll,并初始化JVM;
2、产生一个Bootstrap Loader(启动类加载器);
3、Bootstrap Loader自动加载Extended Loader(标准扩展类加载器),并将其父Loader设为Bootstrap Loader。
4、Bootstrap Loader自动加载AppClass Loader(系统类加载器),并将其父Loader设为Extended Loader。
5、最后由AppClass Loader加载HelloWorld类。
 
四、类加载器的特点
1、运行一个程序时,总是由AppClass Loader(系统类加载器)开始加载指定的类。
2、在加载类时,每个类加载器会将加载任务上交给其父,如果其父找不到,再由自己去加载。 
3、Bootstrap Loader(启动类加载器)是最顶级的类加载器了,其父加载器为null.
 
五、类加载器的获取
很容易,看下面例子
public  class HelloWorld {  
         public  static  void main(String[] args) {  
                 HelloWorld hello =  new HelloWorld();  
                 Class c = hello.getClass();  
                 ClassLoader loader = c.getClassLoader();  
                 System.out.println(loader);  
                 System.out.println(loader.getParent());  
                 System.out.println(loader.getParent().getParent());  
         }  
}
打印结果:
sun.misc.Launcher$AppClassLoader@19821f  
sun.misc.Launcher$ExtClassLoader@addbf1  
null  
从上面的结果可以看出,并没有获取到ExtClassLoader的父Loader,原因是Bootstrap Loader(启动类加载器)是用C语言实现的,找不到一个确定的返回父Loader的方式,于是就返回null。
 
六、类的加载
类加载有三种方式:
1、命令行启动应用时候由JVM初始化加载
2、通过Class.forName()方法动态加载
3、通过Class.getClassLoader().loadClass()方法动态加载
三种方式区别比较大,看个例子就明白了:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
package zhongqiu.common.base;
 
public class ClassLoadDemo {
    static {
        System.out.println("ClassLoadDemo静态初始化块执行了!");
    }
 
    public static void main(String[] args) throws ClassNotFoundException {
        ClassLoader loader2 = ClassLoadDemo.class.getClassLoader();
        System.out.println(loader2);
        // 使用ClassLoader.loadClass()来加载类,不会执行初始化块
        // loader2.loadClass("zhongqiu.test.Test");
        // 使用Class.forName()来加载类,默认会执行初始化块
        // Class.forName("zhongqiu.test.Test");
        // 使用Class.forName()来加载类,并指定ClassLoader,初始化时不执行静态块
        Class.forName("zhongqiu.test.Test"false, loader2);
    }
}

 

七、自定义ClassLoader

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
package zhongqiu.common.base.classload;
 
import java.net.MalformedURLException;
import java.net.URL;
import java.net.URLClassLoader;
 
public class MyClassLoader {
    @SuppressWarnings("resource")
    public static void main(String[] args)
            throws MalformedURLException, ClassNotFoundException, IllegalAccessException, InstantiationException {
        URL url = new URL("file:/D:/javaworkspace/JavaCommon/src/");
        ClassLoader myloader = new URLClassLoader(new URL[] { url });
        Class c = myloader.loadClass("zhongqiu.common.base.classload.Test");
        Test t3 = (Test) c.newInstance();
    }
}

Java.lang包里有个ClassLoader类,ClassLoader 的基本目标是对类的请求提供服务,按需动态装载类和资
源,只有当一个类要使用(使用new 关键字来实例化一个类)的时候,类加载器才会加载这个类并初始化。
一个Java应用程序可以使用不同类型的类加载器。例如Web Application Server中,Servlet的加载使用开发
商自定义的类加载器, java.lang.String在使用JVM系统加载器,Bootstrap Class Loader,开发商定义的其他类
则由AppClassLoader加载。在JVM里由类名和类加载器区别不同的Java类型。因此,JVM允许我们使用不同
的加载器加载相同namespace的java类,而实际上这些相同namespace的java类可以是完全不同的类。这种
机制可以保证JDK自带的java.lang.String是唯一的。

 

八、为什么要使用这种双亲委托模式呢?

  1. 因为这样可以避免重复加载,当父亲已经加载了该类的时候,就没有必要子ClassLoader再加载一次。
  2. 考虑到安全因素,我们试想一下,如果不使用这种委托模式,那我们就可以随时使用自定义的String来动态替代java核心api中定义类型,这样会存在非常大的安全隐患,而双亲委托的方式,就可以避免这种情况,因为String已经在启动时被加载,所以用户自定义类是无法加载一个自定义的ClassLoader。

详解类加载全过程:

 

加载

 

在加载阶段,虚拟机需要完成以下3件事情:

1)通过一个类的全限定名来获取定义此类的二进制字节流。

2)将这个字节流所代表的静态存储结构转化为方法区的运行时数据结构。

3)在内存中生成一个代表这个类的java.lang.Class对象,作为方法区这个类的各种数据的访问接口。

  非数组类的加载是可控性最强的。用户除了使用系统提供的引导类加载类来完成,也可以由用户自定义的类加载器去加载(重写一个类加载器的loadClass())。 

  注意:数组类本身不通过类加载器创建,它是由JVM直接创建的。但数组类和类加载器仍有很紧密的关系,因为数组类的元素类型最终是靠类加载器去创建。 

  加载完成后,虚拟机外部的二进制字节流就按照虚拟机所需格式存储在方法区之中,方法区中的数据存储格式由虚拟机实现自行定义。然后在内存中实例化一个java.lang.Class类的对象(可以在Java堆中,也可以在方法区中),该对象将作为程序去访问方法区中的这些类型数据的外部接口。 

  加载阶段与连接阶段的部分内容(如一部分字节码文件格式验证动作)是交叉进行的,加载阶段尚未结束,连接阶段就可能开始了。但是夹在加载阶段进行的动作,仍然属于连接阶段的内容。

 

验证

  验证是连接的第一步,目的是为了确保Class文件的字节流中包含的信息符合当前虚拟机的要求,并且不会危及虚拟机本身的安全。 

验证阶段的四个步骤:文件格式检验、元数据检验、字节码检验、符号引用检验。

1)文件格式检验: 

检验字节流是否符合Class文件格式的规范,并且能被当前版本的虚拟机处理。检验可能包含下列几种:是否以魔数开头、主次版本号是否在虚拟机的处理范围之内,常量池中的常量是否不被支持、文件是否被删除或附加什么信息等等。 

只有通过文件格式检验的二进制字节流才能进入内存的方法区进行存储,所以后面的3个检验阶段都是基于方法区的存储结构进行的,不会在操作字节流。

2)元数据检验: 

对字节码描述的信息进行语义分析,以保证其描述的内容符合Java语言规范的要求。 

验证点包括:是否有父类(除了object)、父类是否继承了不可被继承的类(被final修饰的类)、如果这个类不是抽象类,是否实现了其父类或接口之中要求实现的所有方法、类中的方法和字段是否与父类产生矛盾(覆盖了父类的final字段、出现不合规矩的方法重载等)。 

元数据检验主要是对类的元数据信息进行语义校验,保证不符合Java语言规范的元数据信息不存在。

3)字节码检验: 

通过数据流和控制流分析,确定程序语义是合法、符合逻辑的。第二阶段是对元数据信息中的数据类型做了检验,这一阶段是对类的方法体进行校验分析,保证被校验类的方法在运行时不会做出危害虚拟机安全的事情。 

检验点包括:保证任意时刻操作数栈的数据类型与指令代码序列都能配合工作、保证指令跳转不会跳转到方法体之外的地方、保证方法体内的类型转换都是有效的。 

事实上,即便是经过字节码检验后的方法体也不一定是安全的。

4)符号引用检验 

最后一个检验发生在虚拟机将符号引用转化为直接引用时,这个转化动作将在连接的第三阶段–解析阶段中发生的。符号引用检验可以看作是对类自身以外(常量池中的各种符号引用)的信息进行匹配性校验。 

校验点:符号引用中通过字符串描述的全限定名是否能找到对应的类、在指定类中是否存在符合方法的字段描述符以及简单名称所描述的方法和字段、符号引用中的类、字段、方法的访问权限是否能让当前类访问到等。 

符号引用检验的目的是确保解析动作的正常执行,如果无法通过符号引用检验,将会抛出java.lang.IncompatibleClassChangeError异常的子类,如IllegalAccessError、NoSuchfiledError、NoSuchMethodError等。

 

准备

  准备阶段是正式为类变量分配内存并设置类变量初始值的阶段。这些变量所使用的内存将在方法区中进行分配。此时进行内存分配的仅包括类变量(被static修饰的变量),而不包括实例变量,实例变量将会在对象实例化时随着对象一起分配在Java堆中。另外,在这里分配的静态类变量是将其值定义为0等默认值,而不是我们定义的。因为这时尚未执行任何Java方法,我们定义的赋值的putStatic指令是程序被编译后,存放在类构造器()方法中,所以正确的赋值将在初始化阶段执行。 

  如果类变量被final修饰,那么在这种情况下,在编译时Javac将会为该变量生成ConstantValue属性,在准备阶段虚拟机会根据该属性设置类变量的正确值。

 

解析

  解析阶段是虚拟机将常量池内的符号引用替换为直接引用的过程。 

a、符号引用:以一组符号来描述所引用的目标,符号可以是任何形式字面量,只要使用时无歧义地定位到目标就行。 

b、 直接引用:直接引用是直接指向目标的指针、相对偏移量或是一个能间接定位到目标的句柄。引用的目标已经在内存中存在。 

  虚拟机实现可以根据需要来判断到底在类被加载器加载时就对常量池中的符号引用进行解析,还是等到一个符号引用将要被使用时才去解析它。解析动作主要针对类或接口、字段、类方法、接口方法、方法类型、方法句柄和调用点限定符7类符号引用进行。

 

初始化

类加载的最后一步,真正执行类中定义的Java程序代码(字节码)。 

初始化阶段是执行类构造器()方法的过程,根据程序员通过程序制定的主观的计划去初始化类变量和其他资源。

 

分享到:
评论

相关推荐

    JVM类加载机制详细讲解

    类加载过程中可能会遇到一些问题,如类冲突、类循环依赖等。类冲突通常发生在不同加载器加载了相同包名下的类,导致版本不一致。类循环依赖则可能引发无限递归,使得类加载失败。解决这些问题通常需要对类加载器的...

    深入Java虚拟机_002_深入详解JVM之类加载器深度剖析、根、扩展及系统类加载器

    本部分我们将深入探讨JVM中的类加载器,特别是根类加载器、扩展类加载器和系统类加载器。 首先,让我们了解类加载的基本过程。当JVM启动时,会触发类加载。这个过程分为三个阶段:加载、链接和初始化。加载阶段,类...

    JVM实战-JVM类加载机制案例分析

    2. **类加载器及类加载器的委托机制**:JVM中有三种内置的类加载器,分别是启动类加载器、扩展类加载器和应用类加载器。此外,还可以自定义类加载器。类加载器之间遵循委托机制,即下级类加载器先请求上级类加载器...

    JVM类加载过程.pptx

    Java代码执行流程是JVM的核心流程之一,它首先通过编译器把Java代码转换成字节码,然后通过类加载器加载到内存中,并将其放在运行时数据区的方法区内。最后,执行引擎将字节码翻译成底层系统指令,再交由CPU去执行。

    JVM:类加载器子系统.pdf

    Java虚拟机(JVM)的类加载器子系统是Java运行时环境的一个重要组成部分,它负责将.class文件加载到内存中,并生成对应的Java类对象。这一过程涵盖了从文件系统或网络获取.class文件、验证类文件的正确性、准备类...

    jvm的类加载机制详解

    jvm的类加载机制详解

    JVM类加载分析

    这篇博客将探讨JVM如何加载、连接和初始化类,以及这一过程中的关键概念和技术。 一、类加载器 类加载器是JVM实现动态加载的核心组件,它的主要任务是根据类名找到对应的.class文件,并将其转换为内存中的Class...

    jvm字节码自动加载

    在Java虚拟机(JVM)中,字节码自动加载是一项关键功能,它使得Java程序能够在运行时动态地发现和加载类。字节码是由Java源代码编译而成的二进制格式,它包含了类和接口的信息。了解JVM如何自动加载字节码对于深入...

    JVM类加载器说明文档

    总结,Java 类加载器是JVM中的重要组成部分,它决定了类的加载过程和加载源,双亲委派机制保证了类加载的有序性和安全性。理解类加载器的工作原理有助于我们更好地进行程序设计和优化,特别是在开发插件系统、模块化...

    JVM底层原理课件PPT

    双亲委派模型是JVM中类加载器之间的关系,每个类加载器都有一个父加载器,除了启动类加载器外,其他类加载器都有自己的父类加载器。当某个特定的类加载器接收到加载请求时,首先将加载任务委托给父类加载器,只有...

    Java虚拟机JVM类加载初始化

    Java虚拟机JVM类加载初始化是Java程序运行过程中的关键环节,它负责将类的字节码文件加载到内存中并进行相应的处理,以便程序能够正确执行。在Java中,类加载器(Classloader)扮演着核心角色。下面将详细讨论类加载...

    jvm 加载class文件

    通过对Java中JVM加载`.class`文件的过程及其类加载器的具体工作原理的介绍,我们可以更加深刻地理解Java程序的运行机制。了解这些基础知识对于开发高质量的Java应用程序至关重要。通过掌握类加载器的工作方式,...

    Java中的JVM类加载机制详解

    JVM的类加载机制是Java程序运行的基础,它包括加载、验证、准备、解析和初始化等阶段。通过理解类加载机制,我们可以更好地管理类的生命周期,实现动态加载和卸载类,以及优化程序的性能。随着Java技术的不断发展,...

    Java虚拟机JVM类加载学习笔记

    JVM的类加载器(ClassLoader)负责将编译后的`.class`文件加载到内存中,为程序执行做好准备。当类被加载时,JVM会在内存的运行时数据区的方法区内存储类的信息,并在堆中创建一个`java.lang.Class`对象来封装这些...

    从JDK源码级别剖析JVM类加载机制

    类加载机制是Java运行时环境的核心功能之一,它负责将.class文件加载到JVM内存中,转换为可以执行的类对象。这个过程分为三个主要阶段:加载、链接和初始化。 1. **加载**: 类加载的第一步是找到类的字节码文件,...

    JVM类加载跟踪器

    JVM类加载跟踪器,用于排查jar包冲突、类冲突、类版本冲突、NoClassDefFoundError、ClassNotFoundException 等等类加载相关问题的辅助工具

    JVM面试宝典,用30分钟搞明白JVM原理

    1、JVM中类加载器分类与核心功能 2、类加载双亲委派机制 3、Tomcat底层类加载是用的双亲委派机制 4、对象完整创建流程 5、对象分配内存时的指针碰撞与空闲列表机制 6、对象分配内存时的并发问题解决CAS与TLAB机制 7...

    Java类加载原理解析

    Java 类加载机制是Java技术体系中的重要组成部分,它关乎到程序运行时的类查找与实例化。当遇到`java.lang.ClassNotFoundException`异常时,通常是因为类加载过程出现了问题。了解类加载机制对于解决这类问题至关...

    JVM加载class文件的原理机制

    JVM加载class文件的原理机制是Java中的核心机制之一,由于Java中的所有类必须被装载到JVM中才能运行,这个装载工作是由JVM中的类装载器完成的。类装载器所做的工作实质是把类文件从硬盘读取到内存中。 类加载机制 ...

Global site tag (gtag.js) - Google Analytics