JVM架构之JVM工作原理

来源:互联网 发布:tradingeconomics数据 编辑:程序博客网 时间:2024/06/05 08:12

原文地址:How JVM Works – JVM Architecture?

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文件是否与类、接口或者枚举有关;
  • 修改器、变量与方法信息等。

.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

连接:性能验证,准备,解析(可选的)。

  • 验证:确保.class文件的正确性。例如要检查这个文件是否被有效的编译器正确地格式化并生成。如果验证失败,那么我们就会到的java.lang.VerifyError异常。
  • 准备:JVM为类的变量分配内存,并用默认值初始化。
  • 解析:这是一个将这个类型的直接引用替换为象征性引用的过程。通过在方法区中的查询来定位引用实体。

初始化:在这一部分,所有的静态变量都用在代码中定义的值和静态块(如果有的话)来赋值。这个执行时从上到下的,从一个类的父类,到继承这个父类的子类。一般来讲,有三种类加载器:

  • Bootstrap类加载器:每个JVM的实现必须有一个Bootstrap类加载器,可以加载可信的类。它加载的是目录JAVA_HOME/jre/lib下的核心Java API。这个路径也是广为人知的Bootstrap路径,它是用像C或者C++这样的语言实现的。
  • 扩展的类加载器(Extension class loader):它是Bootstrap类加载器的孩子,它加载的是扩展路径JAVA_HOME/jre/lib/ext中的类或者是由java.ext.dirs系统属性指定的其他路径的类。它是用sun.misc.Launcher$ExtClassLoader类与Java实现的。
  • 系统/应用类加载器(System/Application class loader):它是扩展类加载器的孩子,主要负责从应用类路径中加载类。它内部用到的环境变量映射到java.class.path,它也是通过sun.misc.Launcher$ExtClassLoader类用Java语言实现的。
// 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都会创建一个运行时栈,并将线程保存在这里。这个栈的每一块被称为活动的记录/栈框架,这里保存方法的调用。方法的所有局部变量全部都保存在相关的框架内。当一个线程结束以后,那么JVM就会销毁运行时栈。它不是共享资源。

程序计数器寄存器(PC Registers):保存一个线程当前执行指令的地址。很显然,每个线程都有独立的程序计数器寄存器。

本地方法栈(Native method stacks):对于每个线程都要建立独立的方法栈,它保存的是本地方法的信息。


这里写图片描述

执行引擎

执行引擎执行的是.class(字节码)文件。它逐行读取字节码,利用在变量存储区的数据和信息表示并执行指令。它可以分为三个部分:

  • 解释器:它逐行解释字节码然后执行。这里有个缺点,当一个方法被调用多次的时候,每次解释器都要执行。
  • 即时编译器(Just-In-Time Compiler(JIT)):它是用于提示解释器的效率的,它编译整个字节码并将其转换为本地代码,所以无论啥时候解释器看到重复的方法调用,JIT都提供直接的本地代码,这样就不需要再次解释了,因此效率得以提升。
  • 垃圾回收:它销毁没有引用的对象。想了解更多,请参考垃圾回收。

Java本地接口(JNI)

它是一个与本地方法库进行交互的接口,并且提供本地库(C、C++)来执行。它可是使JVM调用C/C++库,也可以被已经被硬件指定的C/C++库调用。

本地方法库

它是一个执行引擎所需要的本地库(C/C++)的集合,

0 0
原创粉丝点击