学习Java虚拟机如何工作

64 阅读6分钟

Java虚拟机如何工作

Java虚拟机(或JVM)允许计算机解释或运行Java程序。它作为一个编译器,用于生成机器代码。所有Java程序都需要一个运行时环境。

如果你想让Java应用程序完全运行,你就需要一个JVM(Java虚拟机)。Java代码的主要方法是由JVM调用的,JVM是JRE(Java运行时环境)的一部分。

有一个Java应用程序的俚语叫做WORA(Write Once Run Anywhere)。因此,在一个系统上产生的Java代码可以在任何其他支持Java的系统上运行,而不需要任何改变。

简介

Java虚拟机(JVM)允许所有这些事情发生。具有相同名称的类文件由Java编译器生成,作为一个.java 文件的编译过程的一部分。

这个'.class'文件在执行时执行一连串的任务。下面的步骤描述了JVM的整体情况,我们将在本文中进一步讨论它们。

虚拟机的类型

  • 基于系统的虚拟机(SVM)。作为物理计算机的替代品,SVM被开发出来。一台主机运行它们并使用它们的硬件资源。
  • 基于应用的虚拟机(AVM)。主机允许单个进程作为一个应用程序执行,而不涉及任何硬件。基于进程的虚拟机是它们的另一个名字。JVM属于这组虚拟机,我们将在本文中详细讨论。
  • 应用级虚拟机(AVM)。允许单个进程作为应用程序在主机上执行,没有任何硬件组件。因此,它们也被称为基于进程的虚拟机。

这个.class 文件将JVM分解成许多阶段,描述了它的工作原理。

  • 类加载器
  • JVM内存
  • 执行引擎
  • Java本地接口(JNI)
  • 本地方法库 我们现在将从第一个阶段开始讨论下面的所有这些阶段。

类加载器子系统

类加载器有三个组成部分。

  1. 应用程序类加载器
  2. 扩展类加载器
  3. Bootstrap类加载器

作为JVM的一个阶段,类加载器主要由以下活动组成。

  • 加载。ClassLoader将'.class'文件加载到方法区,并为类文件创建二进制数据。对于JVM来说,每个.class 文件的方法区域包含以下信息,这些信息至关重要。
  • 类是以其超类或根类命名的。
  • .class 文件可以被划分为类或接口。
  • 修改器、变量和方法上有很多细节。

对于每个加载的.class 文件,JVM会产生一个Class对象,在堆内存中代表它。人们应该注意到,生成的这个类对象属于Java.lang 包的。

如果程序员想知道一个类的信息,如它的名字、它的父级、或与之相关的任何方法/变量,他们可以使用这些Class对象。Object类的getClass()函数可以专门用来检索这些对象的引用。JVM构建的类对象在内存中表示.class 文件,在下面这个Java应用程序中演示。

import java.lang.reflect.Field;

import java.lang.reflect.Method;
public class Vehicle {
    public static void main(String[] args)
    {
        car c1 = new car();
        Class newcar1 = newcar1.getClass();
        System.out.println(newcar1.getType());
        Method m[] = newcar1.getDeclaredMethods();
        for (Method method : m)
            System.out.println(method.getType());
            
        Field f[] = newcar1.getDeclaredFields();
        for (Field field : f)
            System.out.println(field.getType());
    }
}
//We can get details about a particular class by using the Class object.
class car {
    private String type;
    private int id_No;
  
    public String getType() { return type; }
    public void setType(String type) { this.type = type; }
    public int getId_no() { return id_No; }
    public void setId_no(int id_no)
    {
        this.id_No = id_no;
    }
}

输出

car
getType
setType
getId_no
setId_no
type
id_No

请记住,加载一个'.class'文件只能创建一个类的实例。

  • 链接。执行三个操作,包括。
  1. 准备:它分配内存并将其初始化为类变量的默认设置。
  2. 验证:只要文件的格式正确,并由一个有效的编译器创建,它就是正确的。当验证失败时,会抛出java.lang 运行时异常。在这种情况下,由ByteCodeVerifier组件负责。之后,该类文件就可以被编译了。
  3. 解决方法:对于符号引用的种类,有一个直接的替代方法,正如其名称所示。在方法区搜索被引用的实体就是这样完成的。

JVM使用Delegation-Hierarchy原则来加载类。加载请求被委托给扩展类加载器,而扩展类加载器又通过系统类加载器将请求委托给引导类加载器。任何其他没有通过扩展类加载器的请求都会通过系统类加载器(如果在引导路径中发现)。作为最后的手段,当系统类加载器不能加载所提供的类时,java.lang 运行时错误被抛出。

JVM内存

RAM也可以被称为Runtime Data Area (RDA)。根据JVM规范的规定,在程序执行过程中,有各种运行时数据区域必须存在。当JVM第一次启动时,会产生几个这样的对象。

可以构造和销毁线程特定对象。线程特定对象分别与一个线程一起生成和销毁。

它通常用于跟踪指令。

总的来说,JVM被分成五个子区域。

  1. 方法区:类的信息,如类名、父类名和方法都保存在方法区。
  2. 堆区:在堆区,所有对象的信息都被保存。每个Java虚拟机也有一个堆区,它也被认为是一种共享的资源。
  3. 栈区:这个堆栈的每一个块都被称为激活记录/堆栈框架,它将方法调用存储在其中的每一个块中。每个局部变量都有一个框架。JVM会在一个线程终止后销毁它的运行时堆栈,由于这个原因,它不是一个可供大众访问的资源。
  4. PC寄存器:存储线程当前指令的地址。每个线程都有其PC寄存器,正如你所期望的那样。
  5. 本机方法区:每个线程都有它的本地堆栈,存储关于本地方法参数的信息。

执行引擎

为了运行一个.class 文件,你需要一个执行引擎。例如,从文件中读取字节码,使用各种内存区域的数据,然后执行指令。

它由下面命名的三个组件组成。

  1. 解释器:本质上,解释器采取字节码并将其翻译成机器代码。解释器在运行一行字节码时可能比执行整个代码时更快。而且,每次调用同一方法时,都必须提供一个新的解释。
  2. JIT编译器(Just In Time compiler):执行引擎使用解释器来执行字节码。JIT编译器将代替执行引擎来检测一个方法是否重复。

JIT编译器由以下部分组成。

  • 中间代码生成器。
  • 代码优化器。
  • 目标代码生成器。
  • 分析器。
  1. 垃圾收集器(GC)。没有被引用的对象可以被垃圾收集器作为后台进程删除,释放出堆上的空间。

Java本地接口(JNI)

与本地方法库交互,提供本地库(C、C++)的执行,特定硬件的C/C++库可以因此而调用和被JVM调用。

本地方法库是一组执行引擎所需的本地库(C、C++)。

结论

虚拟机并不是基于现实的,但它们可以让你觉得它们是基于现实的。当需要编译Java代码时,机器会使用Java虚拟机进行编译,并允许Java应用程序运行。