做网站哪个软件好用,济南搜到网络推广公司,编程软件大全,企业网站建设和管理专栏导航 JVM工作原理与实战 RabbitMQ入门指南 从零开始了解大数据 目录
专栏导航
前言
一、类的生命周期
1.加载#xff08;Loading#xff09;
2.连接#xff08;Linking#xff09;
3.初始化#xff08;Initialization#xff09;
4.使用#xff08;UsingLoading
2.连接Linking
3.初始化Initialization
4.使用Using
5.卸载Unloading
二、初始化阶段
1.初始化阶段分析
2.类的初始化触发方式
3.不执行初始化指令的特殊情况
4.继承关系下的初始化阶段
总结 前言
JVM作为Java程序的运行环境其负责解释和执行字节码管理内存确保安全支持多线程和提供性能监控工具以及确保程序的跨平台运行。本文主要介绍了类的生命周期、类的初始化阶段等内容。 一、类的生命周期
类的生命周期描述了一个类加载、连接、初始化、使用、卸载的整个过程。 1.加载Loading
加载阶段是类的生命周期的起始点。当应用程序首次需要使用某个类时Java虚拟机JVM会负责加载这个类。加载是通过类的加载器ClassLoader完成的它会查找并加载类的二进制数据。这个过程包括将类的字节码从文件系统、JAR文件或网络加载到内存中。
2.连接Linking
连接阶段是加载阶段的后续它包括验证、准备和解析三个子阶段。
验证Verification验证阶段主要是确保被加载的类文件数据符合JVM规范没有安全方面的隐患以及是否与应用程序的其它部分兼容。验证过程包括文件格式验证、元数据验证、字节码验证和符号引用验证。准备Preparation准备阶段是为类的静态变量分配内存并设置默认的初始值。需要注意的是准备阶段并不会执行任何初始化操作。解析Resolution解析阶段是将符号引用转换为直接引用。在Java中符号引用是一个类的全限定名而直接引用是一个直接指向内存中的地址的指针。解析阶段发生在运行时而不是编译时。
3.初始化Initialization
初始化阶段是类加载过程中的最后一步当准备和解析阶段完成后JVM会执行类的构造器方法这个方法是由编译器自动收集类中的所有类变量的赋值动作和静态代码块集合来的。需要注意的是构造器方法中的代码只在类被首次使用时执行一次。
4.使用Using
一旦类被成功加载、连接并初始化后就可以被实例化并用于执行应用程序的业务逻辑。在应用程序运行期间类可能会被频繁地使用。
5.卸载Unloading
当应用程序不再需要某个类时该类的实例以及与其相关的资源将会被回收这个过程就是卸载。但是需要注意的是只有当一个类不再被任何活动对象所引用时它才会被卸载。另外JVM的垃圾回收机制Garbage Collection, GC负责自动处理类的卸载和资源的回收。
二、初始化阶段
1.初始化阶段分析
初始化阶段是类生命周期中的关键阶段之一主要涉及静态代码块的执行和静态变量的初始化。这个阶段是在类首次被加载到内存中或者在首次实例化这个类的对象时发生的。
在初始化阶段会执行静态代码块中的代码这些代码块在类定义中以static声明的部分。这些代码块仅在类首次被加载时执行一次主要用于执行一些仅需在类加载时进行的初始化操作如静态变量的赋值等。
此外初始化阶段会执行字节码文件中clinit部分的字节码指令。clinit方法是Java编译器自动生成的特殊方法用于执行所有类级别的初始化操作包括对静态变量的赋值、静态代码块的执行等。clinit方法的执行顺序与Java源代码中编写的顺序一致确保了按照源代码的顺序进行初始化。
案例
public class Demo1 {public static int value 1;static {value 2;}public static void main(String[] args) {}
}
字节码信息 init构造方法mainMain方法clinit初始化阶段执行
clinit部分的字节码指令 0 iconst_1
1 putstatic #2 init/Demo1.value : I
4 iconst_2
5 putstatic #2 init/Demo1.value : I
8 return
指令解析
iconst_1将常量1放入操作数栈putstatic #2 init/Demo1.value : I从操作数栈中获取值设置到静态变量中
初始化步骤 2.类的初始化触发方式
类的初始化可以通过以下几种方式触发
访问类的静态变量或静态方法。当程序首次访问类的静态变量或静态方法时会触发类的初始化。需要注意的是如果静态变量是final修饰的并且等号右边是常量则不会触发初始化。
案例
public class Demo1 {public static void main(String[] args) {int i Test.i;System.out.println(i);}
}class Test{static {System.out.println(初始化);}public static int i 0;
}
添加-XX:TraceClassLoading 参数可以打印出加载并初始化的类。
-XX:TraceClassLoading 运行结果 调用Class.forName(String className)方法。这个方法用于动态加载类当调用该方法时会触发类的初始化。
Class.forName(String className) 源码 CallerSensitivepublic static Class? forName(String var0) throws ClassNotFoundException {Class var1 Reflection.getCallerClass();return forName0(var0, true, ClassLoader.getClassLoader(var1), var1);}CallerSensitivepublic static Class? forName(String var0, boolean var1, ClassLoader var2) throws ClassNotFoundException {Class var3 null;SecurityManager var4 System.getSecurityManager();if (var4 ! null) {var3 Reflection.getCallerClass();if (VM.isSystemDomainLoader(var2)) {ClassLoader var5 ClassLoader.getClassLoader(var3);if (!VM.isSystemDomainLoader(var5)) {var4.checkPermission(SecurityConstants.GET_CLASSLOADER_PERMISSION);}}}return forName0(var0, var1, var2, var3);}private static native Class? forName0(String var0, boolean var1, ClassLoader var2, Class? var3) throws ClassNotFoundException;
boolean var1参数表示是否初始化这个类 案例
public class Demo1 {public static void main(String[] args) throws ClassNotFoundException {Class? demo2 Class.forName(Test.Demo2);}
}class Demo2 {static {System.out.println(初始化);}
}
运行结果 创建一个类的对象。当使用new关键字创建一个类的对象时会触发类的初始化。执行Main方法的当前类。当Java应用程序的入口点是Main方法时会自动触发当前类的初始化。
案例
public class Demo3 {static {System.out.println(Demo3初始化);}public static void main(String[] args) throws ClassNotFoundException {new Demo4();}
}class Demo4{static {System.out.println(Demo4初始化);}
}
运行结果 3.不执行初始化指令的特殊情况
在字节码层面初始化阶段是通过执行clinit方法来完成的。然而值得注意的是clinit方法并不是在所有情况下都会出现。以下是一些特定情况下不会执行clinit方法的情况
如果类中既没有静态代码块也没有对静态变量进行赋值的语句那么在加载类时不会执行clinit方法。这是因为clinit方法的目的是执行类级别的初始化操作而在这种情况下没有需要进行的初始化工作。
案例
public class Demo1 {public static void main(String[] args) {}
} 字节码信息 如果类中有静态变量的声明但没有对其进行赋值的语句同样不会执行clinit方法。静态变量的初始化需要通过赋值语句来进行如果只是声明但没有赋值则不会被初始化。
案例
public class Demo1 {public static int i;public static void main(String[] args) {}
} 字节码信息 如果静态变量的定义使用了final关键字这类变量会在准备阶段直接进行初始化因此在类加载时不会执行clinit方法。这是因为final修饰的静态变量在编译时就已经确定了值不需要在运行时再进行初始化。
案例
public class Demo1 {public static final int i 1;public static void main(String[] args) {}
} 字节码信息 4.继承关系下的初始化阶段
在继承关系下类的生命周期中的初始化阶段变得更为复杂。子类和父类之间的初始化交互是理解这个过程的关键。
直接访问父类的静态变量在Java中静态变量是类级别的变量它们不属于任何一个对象实例而是与类本身关联。当我们在子类中直接访问父类的静态变量时只会触发父类的初始化而不会触发子类的初始化。这是因为静态变量的初始化是类级别的操作与子类的实例化过程无关。这种机制确保了父类静态变量的初始化在子类中使用之前已经完成。子类的初始化clinit调用之前会先调用父类的clinit初始化方法在Java字节码层面类的初始化是通过执行clinit方法完成的。当一个子类被初始化时它的clinit方法会被调用。但在子类的clinit方法执行之前父类的clinit方法会被先调用。这种顺序确保了父类中的静态变量和静态代码块在子类使用之前已经完成初始化。这种机制确保了继承关系下的正确初始化顺序使得子类可以依赖于父类中定义的静态变量和静态代码块。 总结
JVM是Java程序的运行环境负责字节码解释、内存管理、安全保障、多线程支持、性能监控和跨平台运行。本文主要介绍了类的生命周期、类的初始化阶段等内容希望对大家有所帮助。