龙空技术网

小记:Java类加载过程(Loading)

乌猿 95

前言:

今天你们对“正在载入java”大致比较讲究,朋友们都想要分析一些“正在载入java”的相关知识。那么小编同时在网摘上收集了一些有关“正在载入java””的相关文章,希望咱们能喜欢,咱们一起来了解一下吧!

JVM内存共分为虚拟机栈、堆、方法区、程序计数器、本地方法栈五部分。先将.java文件编译成.class文件,然后通过类加载器将.class文件加载到JVM中,然后再运行。类加载的生命周期包括:加载Loading,验证Verification, 准备Preparation,解析Resolution, 初始化Initialization,使用Using卸载Unloading.除了解析阶段顺序可变,其他阶段顺序都是固定的。加载阶段是将class文件从磁盘或者jar等读到JVM内存中,并为其创建一个class对象,任何一个类被使用时候系统都会为其创建一个Class对象的。加载的同时将加载的这些数据转换成方法区中运行时数据(运行时候数据区:静态变量、静态代码块、常量池等),作为方法区数据的访问入口。new一个对象A,在加载的过程中,会在堆区生成一个代表A类的.class类的对象,这个对象将作为访问方法区中的这些类型数据的外部接口。验证阶段的目的为了确保class文件的字节流中包含的信息符合当前虚拟机的要求,且不会危害到虚拟机自身的安全。一些在编译层面上可以控制的事情(比如超边界访问数组,跨类型进行类型对象转换存在时,编译器是拒绝工作的)。4个阶段的验证过程: 文件格式验证元数据验证字节码验证符号引用验证。准备阶段是正式为类变量分配内存并设置类变量初始值的阶段,这些内存都将在方法区中进行分配。解析阶段解析阶段是虚拟机将常量池内的符号引用替换为直接引用的过程。其实就是将堆内存空间里的静态变量符号修改为已经申请了空间的静态变量地址的过程。初始化阶段是类加载过程的最后一步,初始化阶段才真正开始执行类中定义的JAVA程序代码准备阶段中,变量已经赋过一次系统要求的初始值,而在初始化阶段,则是根据程序员通过程序制定的计划来赋值。或者说,初始化阶段是执行类构造器<clinit>()方法的过程。<clinit>()方法是由编译器自动收集类中的所有类变量的复制动作和静态语句块中的语句合并而成。编译器收集的顺序和语句在源文件中出现的顺序一致静态语句块中只能访问到定义在它之前的变量,定义在它之后的变量,只能赋值,不能访问。虚拟机会保证一个类的<clinit>()方法在多线程环境中正确的加锁同步,如果多个线程同时去初始化一个类,那么只会有一个线程去执行这个类的<clinit>()方法,其他线程都会阻塞,直到该方法执行完,如果在一个类的<clinit>()方法中有耗时很长的操作,可能会造成多个进程阻塞,在实际应用中,这种阻塞往往很隐蔽。类加载器通过一个类的全限定名来获取描述此类的二进制流,执行这个动作的代码模块称为“类加载器”。两个类只有在同一个类加载器加载的前提下才有意思,否则即使两个类原子相同的Class文件,只要加载它们的加载器不同,那这两个类也是不相等的。这里的相等,包括equals,isAssignableFrom(),isInstance() instanceof等情况。双亲委派模型类加载器: 启动类加载器、扩展类加载器、应用程序类加载器、自定义类加载器。双亲委派模型要求除了顶层的启动加载类外,其余的类加载器都应当有自己的父类加载器。这里类加载器之间的父子关系一般 不会以继承的关系来实现,而是 使用组合关系来复用父类加载器的代码。双亲委派模型的工作过程是: 当一个类加载器受到类加载请求,它首先不会自己尝试去加载这个类,而是把这个请求委派给自己的父类加载器去完成,只有当父加载器表示自己无法完成这个加载请求时,子加载器才会尝试自己去加载这个模型的好处,就是保证某个范围的类一定是被某个类加载器所加载的,这就保证在程序中同一个类不会被不同的类加载器加载。这样做的一个主要的考量,就是从安全层面上,杜绝通过使用和JRE相同的类名冒充现有JRE的类达到替换的攻击方式。综上所述Java类的加载过程就是将字节码数据从不同的数据源读取到JVM中,并生成JVM认可的数据结构.class对象。如果输入数据不是 ClassFile 的结构,则会抛出 ClassFormatError。我们可以根据我们自己的需求自定义类加载器。

标签: #正在载入java