JVM(Java虚拟机)充当运行Java应用程序的运行时引擎。JVM实际上调用主要的方法存在于java代码中。JVM是JRE(Java运行时环境)的一部分。
Java应用程序被称为WORA(Write-Once-Run-Anywhere)。这意味着程序员可以在一个系统上开发Java代码,并且可以期望它在任何其他支持Java的系统上运行,而不需要任何调整。这一切都有可能是因为JVM。
当我们编译一个.java语言文件,.class文件存在具有相同类名的文件(包含字节码).java语言文件由Java编译器生成。这个.class当我们运行这个文件时,它会进入不同的步骤。这些步骤共同描述了整个JVM。
1、类加载器子系统
它主要负责三项活动。
- 加载
- 连接
- 初始化
加载:类加载器读取.class文件,生成相应的二进制数据并保存在方法区。在每一个.class文件中,JVM将以下信息存储在Method Area(方法区)中。
- 加载的类及其直接父类的完全限定名。
- .class文件是否与Class或Interface或Enum相关
- 修饰符、变量和方法信息等。
装载.class文件后,JVM会创建一个Class类型的对象来在Heap(堆内存)中表示该文件。请注意,该对象的类型为java.lang包中预定义的Class。这个类对象可以被程序员用来获取类级别的信息,如类名、父类名称、方法和变量信息等。
// 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;
}
}
输出:
Student
getName
setName
getRoll_no
setRoll_no
name
roll_No
注意:对于每个加载的.class文件,仅创建一个Class对象。
Student s2 = new Student();
// c2 will point to same object where
// c1 is pointing
Class c2 = s2.getClass();
System.out.println(c1==c2); // true
链接:执行验证、准备和(可选)解析。
- 验证:它确保.class文件,即检查该文件是否由有效的编译器正确格式化和生成。如果验证失败,我们将得到运行时异常java.lang.verify错误.
- 准备:JVM为类变量分配内存,并将内存初始化为默认值。
- 解析:这是将类型中的符号引用替换为直接引用的过程。它是通过搜索方法区域来定位被引用实体来完成的。
初始化:在这个阶段,所有的静态变量都被分配给代码和静态块(如果有的话)中定义的值。它在类中从上到下执行,在类层次结构中从父到子执行。
一般来说,有三类装载机:
- 引导类加载器(Bootstrap class loader):每个类都必须有一个可信任的引导装入器实现。它加载的核心java API位于JAVA_HOME/jre/lib目录下。此路径通常称为引导路径。它是用C、C++等本机语言实现的。
- 扩展类加载器(Extension class loader):它是引导类加载器的子级。它加载扩展目录中的类JAVA_HOME/jre/lib/ext(扩展路径)或java.ext.dirs系统属性指定的任何其他目录。它由$sun.misc.Launcher$ExtClassLoader class实现
- 系统/应用程序类加载器(System/Application class loader):它是扩展类加载器的子级。它负责从应用程序类路径加载类。它在内部使用映射到java.class.path的环境变量。它也由sun.misc.Launcher$AppClassLoader班级。
// 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());
}
}
输出:
null
sun.misc.Launcher$AppClassLoader@73d16e93
注:JVM遵循委托层次原则来加载类。系统类加载器将加载请求委托给扩展类加载器,而扩展类加载器将请求委托给引导类加载器。若在引导路径中找到类,则类被加载,否则请求再次传输到扩展类装入器,然后再传输到系统类装入器。最后,如果系统类加载器未能加载类,那么我们将得到运行时异常java.lang.ClassNotFoundException.
2、JVM内存
Method Area(方法区域):类的名称、类信息等都是存储在类级的直接变量、类信息等。每个JVM只有一个方法区域,它是一个共享资源。
Heap Area(堆区域):所有对象的信息存储在堆区域中。每个区域也有一个JVM堆。它也是一种共享资源。
Stack Area(栈区域):对于每个线程,JVM创建一个运行时堆栈,它存储在这里。这个堆栈的每个块称为激活记录/堆栈帧,用于存储方法调用。该方法的所有局部变量都存储在相应的帧中。线程终止后,它的运行时堆栈将被JVM销毁。它不是共享资源。
PC Registers(程序寄存器):线程当前执行指令的存储地址。显然每个线程都有独立的PC寄存器,它不是共享资源。
Native Method Stacks(本机方法堆栈):对于每个线程,都会创建单独的本机堆栈。它存储本机方法信息,它不是共享资源。
3、Execution Engine(执行引擎)
执行引擎执行.等级(字节码)。它逐行读取字节码,使用存在于不同内存区的数据和信息并执行指令。它可以分为三个部分:
- Interpreter(解释器):它逐行解释字节码,然后执行。这里的缺点是当一个方法被多次调用时,每次都需要解释。
- Just-In-Time(实时编译器JIT):用于提高解释器的效率,它编译整个字节码并将其更改为本机代码,这样每当解释器看到重复的方法调用时,JIT会直接为该部分提供本机代码,因此不需要重新解释,从而提高了效率。
- Garbage Collector(垃圾收集器):它销毁未引用的对象。有关垃圾回收器的详细信息,请参阅垃圾收集器.
Java Native Interface(Java本机接口JNI):
它提供了一个本机C++库,它提供了一个本机C++执行库。它使JVM可以调用C/C++库,也可以被特定于硬件的C/C++库调用。
Native Method Libraries(本机方法库):
它是执行引擎所需的本机库(C,C++)的集合。
4、JVM简图