本文共 4202 字,大约阅读时间需要 14 分钟。
原文地址:
JVM(Java Virtual Machine)是用来运行Java应用程序的运行时引擎(run-time engine)。JVM就是调用在Java代码中的main方法。JVM是JRE(Java Run Environment)的一部分。
Java应用程序号称“一次写好,处处运行”。这就是说码农可以在一个机器上写好Java代码,不需要任何其他附加手续,就可以在其他任意的Java嵌入系统中运行。能这么搞就是因为有JVM。
当我们要编译一个a.java文件,Java编译器就会生成一个相同名字的a.class文件(包含字节码)。在我们运行这个.class文件的时候,它要经历好几个阶段。这些阶段一起描述了整个JVM。
它主要负责3种活动:
加载:类加载器读取.class文件,生成相应的二进制数据并且将其保存在方法区中。对于每一个.class文件,JVM都要存储下列信息。
.class文件加载完以后,JVM在堆存储区会创建一个这个类类型的对象来表示这个文件。请注意:这个类的对象在java.lang这个包中已经提前定义好了。码农可以通过获取类级别的信息,比如类名,方法与变量信息等利用这个类的对象。我们可以用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 JVMpublic 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; }}
输出:
StudentgetNamesetNamegetRoll_nosetRoll_nonameroll_No
注意:对于每一个加载的.class文件,只能创建这个类的一个对象。
Student s2 = new Student();// c2 will point to same object where // c1 is pointingClass c2 = s2.getClass();System.out.println(c1==c2); // true
连接:性能验证,准备,解析(可选的)。
初始化:在这一部分,所有的静态变量都用在代码中定义的值和静态块(如果有的话)来赋值。这个执行时从上到下的,从一个类的父类,到继承这个父类的子类。一般来讲,有三种类加载器:
// Java code to demonstrate Class Loader subsystempublic 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()); }}
输出:
nullsun.misc.Launcher$AppClassLoader@73d16e93
注意:JVM是遵循委派-分层原则来加载类的。系统类加载器委派加载请求到扩展类加载器,扩展类加载器再委派请求到boot-strap类加载器。如果再boot-strap路径下找到这个类,那么就加载这个类,否则这个请求再次传送到扩展类加载器,然后到系统类加载器。最后如果系统类加载器还是没有加载到类的话,那么就得到运行时异常:java.lang.ClassNotFoundException。
方法区:在方法区中,所有的类级别的信息,例如类名,直接父类名,方法以及变量信息等,全部都要保存,包括静态变量。在每个JVM中有且只有一个方法区,并且它是共享资源。
堆区:堆区存储所有对象的信息。在每个JVM中也只有一个堆区,它也是共享资源。
栈区:对于每一个线程,JVM都会创建一个运行时栈,并将线程保存在这里。这个栈的每一块被称为活动的记录/栈框架,这里保存方法的调用。方法的所有局部变量全部都保存在相关的框架内。当一个线程结束以后,那么JVM就会销毁运行时栈。它不是共享资源。
程序计数器寄存器(PC Registers):保存一个线程当前执行指令的地址。很显然,每个线程都有独立的程序计数器寄存器。
本地方法栈(Native method stacks):对于每个线程都要建立独立的方法栈,它保存的是本地方法的信息。
执行引擎执行的是.class(字节码)文件。它逐行读取字节码,利用在变量存储区的数据和信息表示并执行指令。它可以分为三个部分:
它是一个与本地方法库进行交互的接口,并且提供本地库(C、C++)来执行。它可是使JVM调用C/C++库,也可以被已经被硬件指定的C/C++库调用。
它是一个执行引擎所需要的本地库(C/C++)的集合,