Java虚拟机(JVM)是一种虚拟机,具有自己的指令集架构、内存、栈、堆等特性。它运行在主机操作系统上,并向其提出资源需求。
JVM是一个规范,只要遵循这些规范,就可以有不同的实现方式。该规范可以在以下链接中找到 − https://docs.oracle.com
Oracle有自己的JVM实现(称为HotSpot JVM),IBM也有自己的(例如J9 JVM)。
在规范中定义的操作如下所示(来源−Oracle JVM规范)
JVM(Java虚拟机)架构
HotSpot JVM的架构如图所示
架构包括执行引擎,其中包括垃圾收集器和即时编译器。JVM有两种版本−客户端和服务器端。这两种版本共享相同的运行时代码,但使用的即时编译器不同。用户可以通过指定JVM标志-client或-server来控制使用哪种版本。服务器端的JVM是为长时间运行的应用程序而设计的。
JVM有32位和64位版本。用户可以通过在VM参数中使用-d32或-d64来指定要使用的版本。32位版本只能寻址到4GB的内存。随着关键应用程序需要在内存中维护大型数据集,64位版本满足了这一需求。
JVM(Java虚拟机)架构的组件
以下是JVM(Java虚拟机)架构的主要组件:
1. 类加载器
JVM以动态方式管理类和接口的加载、链接和初始化过程。在加载过程中,JVM查找一个类的二进制表示并创建它。
在链接过程中,加载的类将被组合到JVM的运行时状态中,以便在初始化阶段执行。JVM基本上使用存储在方法区中的运行时常量池中的符号表进行链接过程。初始化则包含实际执行链接的类。
以下是类加载器的类型:
-
启动类加载器:此类加载器位于类加载器层次结构的顶部。它加载标准JDK类,这些类位于JRE的lib目录中。
-
扩展类加载器:此类加载器位于类加载器层次结构的中间,是启动类加载器的直接子级,它加载位于JRE的lib\ext目录中的类。
-
应用程序类加载器:此类加载器位于类加载器层次结构的底部,是应用程序类加载器的直接子级。它加载由CLASSPATH环境变量指定的JAR和类。
2. 链接与初始化
链接过程包含以下三个步骤 −
-
验证:这是由字节码验证器完成的,以确保生成的.class文件(即字节码)有效。如果不是,则会抛出错误并且链接过程停止。
-
准备:为类的所有静态变量分配内存并将它们初始化为默认值。
-
解析:所有符号内存引用都被替换为实际引用。为此,使用了方法区中的类的运行时常量池中存储的符号表。
初始化是类加载过程的最后阶段。静态变量被分配初始值并且执行静态块。
3. 运行时数据区
JVM规范定义了程序执行期间所需的某些运行时数据区。其中一些是在JVM启动时创建的。其他则与线程本地相关,并且仅在创建线程时(以及在线程销毁时)创建。这些列表如下 −
-
PC(程序计数器)寄存器:它是每个线程本地的,并包含线程当前正在执行的JVM指令的地址。
-
栈:它是每个线程本地的,并在方法调用期间存储参数、局部变量和返回地址。如果线程请求比允许的更多的栈空间,则可能会发生栈溢出错误。如果栈可以动态扩展,那么它仍然可能抛出OutOfMemoryError。
-
堆:它被所有线程共享,并包含对象、类元数据、数组等,这些都是在运行时创建的。它在JVM启动时创建并在JVM关闭时销毁。您可以通过某些标志来控制JVM从操作系统请求的堆的大小(稍后会有更多相关内容)。需要注意的是不要请求太少或太多的内存,因为这对性能有重要影响。此外,GC管理这个空间并持续移除无用的对象以释放空间。
-
方法区:这个运行时区域为所有线程所共有,并在JVM启动时创建。它存储每类结构,如常量池(稍后会有更多相关内容)、构造函数和方法的代码、方法数据等。JLS没有规定是否需要对此区域进行垃圾收集,因此JVM的实现可以选择忽略GC。此外,这可能会或不会根据应用程序的需求进行扩展。JLS在这方面没有规定任何强制性要求。
-
运行时常量池:JVM为每个类/每个类型维护一个数据结构,作为链接加载类时的符号表(这是它的众多角色之一)。
-
本地方法栈:当线程调用一个本地方法时,它进入了一个新的世界,在这个世界里,Java虚拟机的结构和安全限制不再阻碍其自由。一个本地方法可能访问虚拟机的运行时数据区(这取决于本地方法接口),但也可能做任何其他想要做的事情。
4. 执行引擎
执行引擎负责执行字节码,它有三个不同的组成部分:
-
垃圾收集:JVM管理Java中对象的整个生命周期。一旦创建了一个对象,开发人员就无需再担心它。如果对象变得无用(也就是说,不再有任何引用指向它),则它将通过GC使用多种算法之一从堆中移除。
-
解释器:解释器解释字节码。虽然它快速解释代码,但在执行时较慢。
-
即时编译器:JIT代表Just-In-Time。JIT编译器是Java运行环境的主要部分之一,它在运行时将字节码编译成机器码。
5. Java本地接口(JNI)
Java本地接口(JNI)与本地方法库交互,这些库对于执行至关重要。
6. 本地方法库
本地方法库是一组C和C++库(本地库),对于执行至关重要。