类加载的时机(底下几个例子的说明)
JVM加载class文件的原理
Java虚拟机与程序的生命周期
一个运行时的Java虚拟机(JVM)负责运行一个Java程序。
当启动一个Java程序时,一个虚拟机实例诞生;当程序关闭退出,这个虚拟机实例也就随之消亡。
如果在同一台计算机上同时运行多个Java程序,将得到多个Java虚拟机实例,每个Java程序都运行于它自己的Java虚拟机实例中。
程序执行之前,会进行类的加载、连接与初始化
1.加载
查找并加载类的二进制数据。
2.连接
连接又分为三个步骤:
验证:确保被加载类的正确性。
即验证class文件是否符合JVM的要求。
准备:为类的静态变量分配内存,并将其初始化为默认值。
解析:把类中的符号引用转换为直接引用。
java虚拟机中的符号引用和直接引用
在java中,一个java类将会编译成一个class文件。在编译时,java类并不知道引用类的实际内存地址,因此只能使用符号引用来代替。比如org.simple.People类引用org.simple.Tool类,在编译时People类并不知道Tool类的实际内存地址,因此只能使用符号org.simple.Tool(假设)来表示Tool类的地址。而在类装载器装载People类时,此时可以通过虚拟机获取Tool类 的实际内存地址,因此便可以既将符号org.simple.Tool替换为Tool类的实际内存地址,及直接引用地址。
3.初始化
为类的静态变量赋予正确的初始值,即在程序里为静态变量指定的初始值,或静态代码块中的赋值操作。
静态代码块是从上到下顺序执行的,可以对一个静态变量多次赋值,最后的结果为静态变量的初始值。
每步解析:
1、类的加载概述
类的加载指的是将类的.class文件中的二进制数据读入到内存中,将其放在运行时数据区的方法区内,然后在堆区创建一个java.lang.Class对象,用来封装类在方法区内的数据结构。
类加载器
加载器有两种类型:
1.Java虚拟器自带的加载器
根类加载器(Bootstrap)
扩展类加载器(Extension)
系统类加载器或称应用加载器(System/APP)
后两种加载器是Java实现的,根类加载器是C++写的,程序员无法在Java代码中获得该类。
2.用户自定义的类加载器
java.lang.ClassLoader的子类
用户可以定制类的加载方式
类加载器并不需要等到某个类被首次主动使用时再加载它。
JVM规范允许类加载器在预料某个类将要被使用时就预先加载它,如果在预先加载的过程中遇到了.class文件缺失或存在错误,类加载器必须在程序首次主动使用该类时才报告错误(LinkageError)。如果这个类一直没有被程序主动使用,那么类加载器就不会报告错误。
JVM加载类的过程
当我们使用命令来执行某一个Java程序(比如Test.class)的时候:java Test (1) java.exe 会帮助我们找到 JRE ,接着找到位于 JRE 内部的 jvm.dll ,这才是真正的 Java 虚拟机器 , 最后加载动态库,激活 Java 虚拟机器。
(2) 虚拟机器激活以后,会先做一些初始化的动作,比如说读取系统参数等。一旦初始化动作完成之后,就会产生第一个类装载器 ―― Bootstrap Loader(启动类装载器 ) (3) Bootstrap Loader 所做的初始工作中,除了一些基本的初始化动作之外,最重要的就是加载 Launcher.java 之中的 ExtClassLoader(扩展类装载器) ,并设定其 Parent 为 null ,代表其父加载器为 BootstrapLoader 。 (4) 然后 Bootstrap Loader 再要求加载 Launcher.java 之中的 AppClassLoader(用户自定义类装载器 ) ,并设定其 Parent 为之前产生的 ExtClassLoader 实体。这两个加载器都是以静态类的形式存在的。 这里要请大家注意的是, Launcher$ExtClassLoader.class 与 Launcher$AppClassLoader.class 都是由 Bootstrap Loader 所加载,所以 Parent 和由哪个类加载器加载没有关系。
2、连接过程:验证、准备、解析
连接就是将已经读入到内存的类的二进制数据合并到虚拟机的运行时环境中去。
连接阶段三个步骤:验证、准备和解析。
类的验证
类的验证内容:
1.类文件的结构检查
确保类文件遵从Java类文件的固定格式。
2.语义检查
确保类本身符合Java语言的语法规定,比如验证final类型的类没有子类,以及final类型的方法没有被覆盖。
注意,语义检查的错误在编译器编译阶段就会通不过,但是如果有程序员通过非编译的手段生成了类文件,其中有可能会含有语义错误,此时的语义检查主要是防止这种没有编译而生成的class文件引入的错误。
3.字节码验证
确保字节码流可以被Java虚拟机安全地执行。
字节码流代表Java方法(包括静态方法和实例方法),它是由被称作操作码的单字节指令组成的序列,每一个操作码后都跟着一个或多个操作数。
字节码验证步骤会检查每个操作码是否合法,即是否有着合法的操作数。
4.二级制兼容性的验证
确保相互引用的类之间的协调一致。
例如,在Worker类的gotoWork()方法中会调用Car类的run()方法,Java虚拟机在验证Worker类时,会检查在方法区内是否存在Car类的run()方法,假如不存在(当Worker类和Car类的版本不兼容就会出现这种问题),就会抛出NoSuchMethodError错误。
类的准备
在准备阶段,Java虚拟机为类的静态变量分配内存,并设置默认的初始值。
例如对于以下Sample类,在准备阶段,将为int类型的静态变量a分配4个字节的内存空间,并且赋予默认值0,为long类型的静态变量b分配8个字节的内存空间,并且赋予默认值0。
public class Sample {
private static int a = 1;
private static long b;
static {
b = 2;
}
}
类的解析
在解析阶段,Java虚拟机会把类的二级制数据中的符号引用替换为直接引用。
例如在Worker类的gotoWork()方法中会引用Car类的run()方法。
public void gotoWork() {
car.run();// 这段代码在Worker类的二进制数据中表示为符号引用
}
在Worker类的二进制数据中,包含了一个对Car类的run()方法的符号引用,它由run()方法的全名和相关描述符组成。
在解析阶段,Java虚拟机会把这个符号引用替换为一个指针,该指针指向Car类的run()方法在方法区内的内存位置,这个指针就是直接引用。
3、类的初始化
类的初始化步骤
1.假如这个类还没有被加载和连接,那就先进行加载和连接。
2.假如类存在直接的父类,并且这个父类还没有被初始化,那就先初始化直接的父类。
3.假如类中存在初始化语句,那就依次执行这些初始化语句。
接口的特殊性
当Java虚拟机初始化一个类时,要求它的所有父类都已经被初始化,但是这条规则并不适用于接口。
在初始化一个类时,并不会先初始化它所实现的接口。
在初始化一个接口时,并不会先初始化它的父接口。
因此,一个父接口并不会因为它的子接口或者实现类的初始化而初始化,只有当程序首次使用特定接口的静态变量时,才会导致该接口的初始化。
final类型的静态变量
final类型的静态变量是编译时常量还是变量,会影响初始化语句块的执行。
如果一个静态变量的值是一个编译时的常量,就不会对类型进行初始化(类的static块不执行);
如果一个静态变量的值是一个非编译时的常量,即只有运行时会有确定的初始化值,则就会对这个类型进行初始化(类的static块执行)。
import java.util.Random;
class FinalTest1 {
public static final int x = 6 / 3; // 编译时期已经可知其值为2,是常量
// 类型不需要进行初始化
static {
System.out.println("static block in FinalTest1");
// 此段语句不会被执行,即无输出
}
}
class FinalTest2 {
public static final int x = new Random().nextInt(100);// 只有运行时才能得到值
static {
System.out.println("static block in FinalTest2");
// 会进行类的初始化,即静态语句块会执行,有输出
}
}
public class InitTest {
public static void main(String[] args) {
System.out.println("FinalTest1: " + FinalTest1.x);
System.out.println("FinalTest2: " + FinalTest2.x);
}
}
结果:
FinalTest1: 2
static block in FinalTest2
FinalTest2: 11
主动使用的归属明确性
只有当程序访问的静态变量或静态方法确实在当前类或当前接口中定义时,才可以认为是对类或接口的主动使用。(此处暂时木看懂。。)
class Parent {
static int a = 3;
static {
System.out.println("Parent static block");
}
static void doSomething() {
System.out.println("do something");
}
}
class Child extends Parent {
static {
System.out.println("Child static block");
}
}
public class ParentTest {
public static void main(String[] args) {
System.out.println("Child.a: " + Child.a);
Child.doSomething();
// Child类的静态代码块没有执行,说明Child类没有初始化
// 这是因为主动使用的变量和方法都是定义在Parent类中的
}
}
结果:
Parent static block
Child.a: 3
do something
这是因为对于静态字段,只有直接定义该字段的类才会被初始化,因此当我们通过子类来引用父类中定义的静态字段时,只会触发父类的初始化,而不会触发子类的初始化。
ClassLoader类
调用ClassLoader类的loadClass()方法加载一个类,并不是对类的主动使用,不会导致类的初始化。
package com.test.controller;
class CL {
static {
System.out.println("static block in CL");
}
}
public class ClassLoaderInitTest {
public static void main(String[] args) throws Exception {
ClassLoader loader = ClassLoader.getSystemClassLoader();
Class<?> clazz = loader.loadClass("com.test.controller.CL");
// loadClass方法加载一个类,并不是对类的主动使用,不会导致类的初始化
System.out.println("----------------");
clazz = Class.forName("com.test.controller.CL");
}
}
结果:
----------------
static block in CL
相关推荐
总结,JVM 类加载机制是Java平台的核心特性之一,它确保了程序的稳定运行和动态扩展能力。理解类加载器的工作原理和双亲委派模型对于优化程序性能、解决类冲突以及构建复杂的模块化系统至关重要。在实际开发中,掌握...
### JVM实战-JVM类加载机制案例分析 #### 实验背景与目标 本次实验的主要目的是深入理解Java虚拟机(JVM)中的类加载机制。通过实践操作,掌握类的加载、连接与初始化过程,了解不同类型的类加载器及其工作原理,...
JVM的类加载机制是JVM的核心机制之一,它把描述类的数据从class文件加载到内存,并对数据进行校验、转换解析和初识化,形成可以被虚拟机直接使用的Java类型。 Java代码执行流程是JVM的核心流程之一,它首先通过...
Java虚拟机(JVM)的类加载机制是Java运行时环境的重要组成部分,它负责将类的字节码文件加载到内存中,进行一系列处理并使其成为可执行的Java类型。这个过程包括五个主要阶段:加载、验证、准备、解析和初始化。 1...
锁以及jvm类加载机制
JVM的类加载机制是Java程序运行的基础,它包括加载、验证、准备、解析和初始化等阶段。通过理解类加载机制,我们可以更好地管理类的生命周期,实现动态加载和卸载类,以及优化程序的性能。随着Java技术的不断发展,...
Java虚拟机(JVM)是Java程序运行的基础,它的类加载机制是理解Java应用程序如何启动和执行的关键部分。本文将深入JDK源码,详细解析JVM类加载机制的各个环节,帮助开发者更好地理解和优化自己的代码。 类加载机制...
在Java开发中,JVM(Java虚拟机)的类加载机制是至关重要的,因为它负责将类的字节码转换为运行时的实例。本专题"性能调优专题-jvm类加载机制-performance-jvmclassloader"深入探讨了如何通过理解并优化类加载过程来...
jvm的类加载机制详解
Java虚拟机(JVM)是Java程序运行的核心,而类加载机制则是JVM实现程序动态加载和执行的关键部分。在深入理解Java虚拟机的工作原理时,类加载机制是一个不可忽视的重要知识点。这篇博客将探讨JVM如何加载、连接和...
了解类加载机制对于解决这类问题至关重要,同时也有助于深入理解Java虚拟机(JVM)的工作原理。 Java 类加载机制主要由类加载器完成。JVM内置了三种预定义的类加载器: 1. **启动类加载器(Bootstrap ClassLoader...
本文将深入探讨JVM的四个关键领域:类的加载机制、内存结构、垃圾回收(GC)算法以及GC分析和命令调优。 首先,我们来看类的加载机制。类的加载始于加载阶段,它将类的.class文件中的二进制数据读入内存,并在方法...
Java虚拟机(JVM)的类加载机制是Java应用程序运行灵活性的关键。类加载涉及多个步骤,包括加载、验证、准备、解析、初始化、使用和卸载。这些阶段确保了类的正确加载、验证其安全性和有效运行。 1. **加载...
"详解JVM类加载机制及类缓存问题的处理方法" JVM类加载机制是Java虚拟机(JVM)加载类的过程。该过程包括加载、验证、准备、解析、初始化、使用和卸载七个步骤。类加载机制是JVM根据所需的class文件的路径,通过IO...
### 深入研究Java类加载机制 #### 一、Java类加载机制概述 Java类加载机制是Java程序运行的第一步,它对于理解Java虚拟机(JVM)的行为至关重要。类加载过程涉及到类的加载、链接(验证、准备、解析)、初始化等...
《JAVA-JVM-01类加载机制》 Java虚拟机(JVM)是Java程序运行的基础,其中类加载机制是其核心组成部分。本文将深入剖析Java中的类加载器和双亲委派机制,并通过示例讲解如何自定义类加载器。 类加载过程是Java程序...
在JVM的学习中,理解其内存模型、垃圾收集算法以及类加载机制至关重要。 1. **JVM内存模型** - **方法区**:也称为“永久代”,存储虚拟机加载的类信息、常量、静态变量等,是线程共享的区域。在Java 8之后,这...
"Jvm类加载机制详解" Jvm类加载机制是Java虚拟机(JVM)中一个非常重要的机制,它负责将编译后的Java类文件加载到JVM中,并对其进行验证、准备、解析、初始化和卸载等操作。 类加载机制的生命周期可以分为五个阶段...
【JVM】类的奇幻漂流——类加载机制探秘 Java虚拟机(JVM)是运行Java程序的核心组件,它负责将我们编写的类加载到内存中并执行。类加载机制是JVM的一个重要组成部分,它确保了程序的正常运行。本文将带你深入理解...