深入了解JVM的工作原理

JVM(Java 虚拟机)充当运行 Java 应用程序的运行时引擎。JVM 实际上是调用Java 代码中存在的主要方法的虚拟机。JVM 是 JRE(Java 运行时环境)的一部分。

Java 应用程序被称为 WORA(一次编写,随处运行)。这意味着程序员可以在一个系统上开发 Java 代码,并且可以期望它在任何其他支持 Java 的系统上运行而无需进行任何调整。这一切都是因为 JVM 而成为可能。

当我们编译 .java 文件时,Java 编译器会生成与 .java文件中的类名相同的 .class 文件(包含字节码) 。运行此 .class文件时,它会经历多个步骤。这些步骤共同描述了整个 JVM。

类加载器子系统

主要负责三项活动。

  • 加载中
  • 链接
  • 初始化

加载:*类加载器读取".class" 文件,生成相应的二进制数据并保存在方法区中。对于每一个" *.class" 文件,JVM在方法区中存储以下信息。

  • 已加载类及其直接父类的完全限定名称。
  • " *.class" 文件是否与 Class 或 Interface 或 Enum 相关。
  • 修饰符、变量和方法信息等。

加载 *.class" 文件后,JVM 会创建一个 Class 类型的对象来表示堆内存中的此文件。请注意,此对象是java.lang包中预定义的 Class 类型。程序员可以使用这些 Class 对象来获取类级信息,如类的名称、父名称、方法和变量信息等。要获取此对象引用,我们可以使用Object类的**getClass() *方法。

java 复制代码
// A Java program to demonstrate working
// of a Class type object created by JVM
// to represent .class file in memory.
import java.lang.reflect.Field;
import java.lang.reflect.Method;

// Java code to demonstrate use
// of Class object created by JVM
public class Test {
	public static void main(String[] args)
	{
		Student s1 = new Student();

		// Getting hold of Class
		// object created by JVM.
		Class c1 = s1.getClass();

		// Printing type of object using c1.
		System.out.println(c1.getName());

		// getting all methods in an array
		Method m[] = c1.getDeclaredMethods();
		for (Method method : m)
			System.out.println(method.getName());

		// getting all fields in an array
		Field f[] = c1.getDeclaredFields();
		for (Field field : f)
			System.out.println(field.getName());
	}
}

// A sample class whose information
// is fetched above using its Class object.
class Student {
	private String name;
	private int roll_No;

	public String getName() { return name; }
	public void setName(String name) { this.name = name; }
	public int getRoll_no() { return roll_No; }
	public void setRoll_no(int roll_no)
	{
		this.roll_No = roll_no;
	}
}

输出

java 复制代码
学生
获取名称
设置名称
获取Roll_no
设置Roll_no
姓名
roll_No

注意: 对于每个加载的" **.class"文件,只会创建该类的 一个对象。

java 复制代码
Student s2 = new Student(); // c2 将指向c1 指向的
同一对象Class c2 = s2.getClass(); System.out.println(c1==c2); // true

链接: 执行验证、准备和(可选)解析。

  • 验证:它确保 .class 文件的正确性,即检查此文件是否格式正确且是否由有效的编译器生成。如果验证失败,我们会得到运行时异常java.lang.VerifyError。此活动由组件 ByteCodeVerifier 完成。一旦此活动完成,类文件即可进行编译。
  • 准备:JVM 为类静态变量分配内存并将内存初始化为默认值。
  • 解析:这是用直接引用替换类型的符号引用的过程。通过搜索方法区来定位引用的实体。

初始化: 在此阶段,所有静态变量都将被赋予在代码和静态块(如果有)中定义的值。在类中,此过程从上到下执行,在类层次结构中,从父类到子类执行。

一般来说,有三个类加载器:

  • 引导类加载器 :每个 JVM 实现都必须有一个引导类加载器,能够加载受信任的类。它加载" *JAVA_HOME/jre/lib" *目录中的核心 Java API 类。此路径通常称为引导路径。它以 C、C++ 等本机语言实现。
  • 扩展类加载器 :它是引导类加载器的子类。它加载扩展目录" JAVA_HOME/jre/lib/ext" (扩展路径)或 java.ext.dirs 系统属性指定的任何其他目录中的类。它在 Java 中由 sun.misc.Launcher$ExtClassLoader类实现。
  • 系统/应用程序类加载器 :它是扩展类加载器的子类。它负责从应用程序类路径加载类。它在内部使用映射到 java.class.path 的环境变量。它也由sun.misc.Launcher$AppClassLoader类在 Java 中实现。
java 复制代码
// Java code to demonstrate Class Loader subsystem
public class Test {
	public static void main(String[] args)
	{
		// String class is loaded by bootstrap loader, and
		// bootstrap loader is not Java object, hence null
		System.out.println(String.class.getClassLoader());

		// Test class is loaded by Application loader
		System.out.println(Test.class.getClassLoader());
	}
}

输出

js 复制代码
无效的
jdk.internal.loader.ClassLoaders$AppClassLoader@8bcc55f

**注意: JVM 遵循委托层次原则来加载类。系统类加载器将加载请求委托给扩展类加载器,扩展类加载器将请求委托给引导类加载器。如果在引导路径中找到类,则加载该类,否则请求再次转移到扩展类加载器,然后转移到系统类加载器。最后,如果系统类加载器无法加载类,则我们会得到运行时异常**java.lang.ClassNotFoundException。

JVM 内存

  1. 方法区: 方法区中存储了所有类级别的信息,包括类名、直接父类名、方法和变量信息等,包括静态变量。每个 JVM 只有一个方法区,并且它是一个共享资源。
  2. 堆区域: 所有对象的信息都存储在堆区域中。每个 JVM 也有一个堆区域。它也是共享资源。
  3. 堆栈区域: 对于每个线程,JVM 都会创建一个运行时堆栈并存储在此处。此堆栈的每个块称为激活记录/堆栈框架,用于存储方法调用。该方法的所有局部变量都存储在其相应的框架中。线程终止后,其运行时堆栈将被 JVM 销毁。它不是共享资源。
  4. PC寄存器: 存放线程当前执行指令的地址,显然每个线程都有独立的PC寄存器。
  5. 本机方法堆栈: 对于每个线程,都会创建一个单独的本机堆栈。它存储本机方法信息。

执行引擎

执行引擎执行" *.class" *(字节码)。它逐行读取字节码,使用各个内存区域中存在的数据和信息并执行指令。它可以分为三个部分:

  • 解释器:它将字节码逐行解释然后执行。这里的缺点是当一个方法被多次调用时,每次都需要解释。
  • *即时编译器 (JIT) *:用于提高解释器的效率。它编译整个字节码并将其更改为本机代码,因此每当解释器看到重复的方法调用时,JIT 都会为该部分提供直接本机代码,因此不需要重新解释,从而提高了效率。
  • 垃圾收集器:它会销毁未引用的对象。有关垃圾收集器的更多信息,请参阅垃圾收集器

Java 本机接口(JNI):

它是一个与本机方法库交互的接口,提供执行所需的本机库(C、C++)。它使 JVM 能够调用 C/C++ 库,并被可能特定于硬件的 C/C++ 库调用。

本机方法库:

它是执行引擎所需的本机库(C,C ++)的集合。

相关推荐
天天扭码12 分钟前
五天SpringCloud计划——DAY2之单体架构和微服务架构的选择和转换原则
java·spring cloud·微服务·架构
程序猿进阶13 分钟前
堆外内存泄露排查经历
java·jvm·后端·面试·性能优化·oom·内存泄露
FIN技术铺18 分钟前
Spring Boot框架Starter组件整理
java·spring boot·后端
小曲程序25 分钟前
vue3 封装request请求
java·前端·typescript·vue
陈王卜42 分钟前
django+boostrap实现发布博客权限控制
java·前端·django
小码的头发丝、43 分钟前
Spring Boot 注解
java·spring boot
java亮小白19971 小时前
Spring循环依赖如何解决的?
java·后端·spring
飞滕人生TYF1 小时前
java Queue 详解
java·队列
武子康1 小时前
大数据-230 离线数仓 - ODS层的构建 Hive处理 UDF 与 SerDe 处理 与 当前总结
java·大数据·数据仓库·hive·hadoop·sql·hdfs
武子康1 小时前
大数据-231 离线数仓 - DWS 层、ADS 层的创建 Hive 执行脚本
java·大数据·数据仓库·hive·hadoop·mysql