JVM如何工作 – JVM体系结构?
JVM(Java虚拟机)充当运行Java应用程序的运行时引擎。JVM实际上是调用java代码中的主要方法。JVM是JRE(Java Run Environment)的一部分。
Java应用程序被称为WORA(一次写入运行)。这意味着程序员可以在一个系统上开发Java代码,并且可以期望它可以在没有任何调整的情况下在任何其他启用Java的系统上运行。这是因为JVM所有可能的。
当我们编译一个.java文件时,Java编译器会生成一个具有相同文件名的.class文件(包含字节码)。当我们运行它时,这个.class文件会进入各个步骤。这些步骤一起描述了整个JVM。
类加载器子系统
它主要负责三项活动。
- 载入中
- 链接
- 初始化
加载:类加载器读取.class文件,生成相应的二进制数据并将其保存在方法区域中。对于每个.class文件,JVM将以下信息存储在方法区域中。
- 加载的类及其直接父类的完全限定名称。
- 无论的.class文件与类或接口或枚举
- 修饰符,变量和方法信息等。
加载.class文件后,JVM创建一个Class类型的对象来表示堆文件中的这个文件。请注意,此对象的类型是java.lang包中预定义的类。这个Class对象可以被程序员用来获取类名称,父名称,方法和变量信息等类的级别信息。为了得到这个对象引用,我们可以使用Object类的getClass()方法。
// 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.VerifyError。
- 准备工作:JVM为类变量分配内存并将内存初始化为默认值。
- 解决方法:这是用直接引用替换类型中的符号引用的过程。它通过搜索方法区域来查找引用的实体。
初始化:在这个阶段,所有的静态变量都被分配了它们在代码和静态块中定义的值(如果有的话)。这是在类中从上到下执行的,并且在类层次结构中从父到子执行。
一般来说,有三类装载机:
- Bootstrap类加载器:每个JVM实现都必须有一个引导类加载器,能够加载受信任的类。它加载存在于JAVA_HOME / jre / lib目录中的核心Java API类。这条路通常被称为自举路径。它用C,C ++等本地语言实现。
- 扩展类加载器:它是引导类加载器的子元素。它加载扩展目录JAVA_HOME / jre / lib / ext(扩展路径)或由java.ext.dirs系统属性指定的任何其他目录中存在的类。它由sun.misc.Launcher $ ExtClassLoader类在java中实现。
- 系统/应用程序类加载器:它是扩展类加载器的子代。它负责从应用程序类路径加载类。它在内部使用映射到java.class.path的环境变量。它也是由sun.misc.Launcher $ AppClassLoader类在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()); } }
输出:
null sun.misc.Launcher$AppClassLoader@73d16e93
注意: JVM遵循Delegation-Hierarchy原则来加载类。系统类加载器将加载请求委托给扩展类加载器和扩展类加载器委托请求引导类加载器。如果在引导路径中找到类,则类将被加载,否则请求会再次传输到扩展类加载器,然后传递到系统类加载器。最后,如果系统类加载器未能加载类,那么我们会得到运行时异常java.lang.ClassNotFoundException。
JVM内存
方法区:在方法区中,存储所有类级别的信息,如类名,直接父类名,方法和变量信息等,包括静态变量。每个JVM只有一个方法区域,它是一个共享资源。堆区:所有对象的信息存储在堆区中。每个JVM还有一个堆区。这也是一个共享资源。
堆栈区:对于每个线程,JVM创建一个存储在此处的运行时堆栈。这个堆栈的每个块被称为存储方法调用的激活记录/堆栈帧。该方法的所有局部变量都存储在相应的框架中。线程终止后,运行时堆栈将被JVM销毁。它不是共享资源。
PC寄存器:存储线程当前执行指令的地址。显然每个线程都有独立的PC寄存器。
本地方法堆栈:对于每个线程,都会创建单独的本地堆栈。它存储本机方法信息。
执行引擎
执行引擎执行.class(字节码)。它逐行读取字节码,使用各种存储区中的数据和信息并执行指令。它可以分为三部分: -
- 解释器:它逐行解释字节码,然后执行。这里的缺点是,当多次调用一个方法时,每次需要解释。
- 即时编译器(JIT):它用于提高解释器的效率。它编译整个字节码并将其更改为本地代码,因此无论何时解释器看到重复的方法调用,JIT都会为该部分提供直接的本机代码,以便重新解释不是必需的,因此提高了效率。
- 垃圾收集器:它销毁未引用的对象。有关垃圾收集器的更多信息,请参阅垃圾收集器。
Java本地接口(JNI):
它是一个与Native Method Libraries交互的接口,并提供执行所需的本地库(C,C ++)。它使JVM能够调用C / C ++库并通过可能特定于硬件的C / C ++库进行调用。
本地方法库:
它是执行引擎所需的本地库(C,C ++)的集合。