📜  JVM如何工作–JVM体系结构

📅  最后修改于: 2020-02-10 15:07:58             🧑  作者: Mango

JVM(Java虚拟机)充当运行Java应用程序的运行时引擎。JVM是实际上调用Java代码中存在的main方法的一种。JVM是JRE(Java运行时环境)的一部分。
Java应用程序称为WORA(可在任何地方写入一次)。这意味着程序员可以在一个系统上开发Java代码,并且可以期望它在任何其他启用Java的系统上运行,而无需进行任何调整。由于JVM,所有这些都是可能的。
当我们编译.java文件时,Java编译器会生成具有与.java文件中相同的类名的.class文件(包含字节码)。当我们运行它时,此.class文件进入各个步骤。这些步骤共同描述了整个JVM:

 
类加载器子系统
它主要负责三个活动。

  • 载入
  • 链接
  • 初始化

加载:类加载器读取.class文件,生成相应的二进制数据并将其保存在方法区域中。对于每个.class文件,JVM将以下信息存储在方法区域中。

  • 加载的类及其直接父类的全限定名称。
  • .class文件与类或接口或枚举
  • 修饰符,变量和方法信息等

加载.class文件后,JVM创建类型为Class的对象来表示该文件在堆内存中。请注意,该对象的类型为java.lang包中预定义的Class 。程序员可以使用此Class对象来获取类级别的信息,例如类名称,父名称,方法和变量信息等。要获取此对象引用,我们可以使用Object类的getClass()方法。

// A Jav代码,展示被JVM创建的类实例中,代表.class且被储蓄在内存中的信息
import java.lang.reflect.Field;
import java.lang.reflect.Method;
// 创建测试类
public class Test
{
    public static void main(String[] args)
    {
        Student s1 = new Student();
        // 得到claas信息
        Class c1 = s1.getClass();
        // 打印c1类型
        System.out.println(c1.getName());
        // 在一个array中得到所有防范
        Method m[] = c1.getDeclaredMethods();
        for (Method method : m)
            System.out.println(method.getName());
        // 在一个array中,得到所有fields
        Field f[] = c1.getDeclaredFields();
        for (Field field : f)
            System.out.println(field.getName());
    }
}
// 测试代码
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和c1指向同一个实例对象
Class c2 = s2.getClass();
System.out.println(c1==c2); // true

链接:执行验证,准备和(可选)解决方案。

  • 验证:它确保.class文件的正确性,即检查该文件的格式是否正确,是否由有效的编译器生成。如果验证失败,我们将获得运行时异常java.lang.VerifyError
  • 准备:JVM为类变量分配内存,并将内存初始化为默认值。
  • 解决方案:这是将类型中的符号引用替换为直接引用的过程。通过搜索方法区域以找到引用的实体来完成此操作。

初始化:在此阶段,所有静态变量都分配有在代码和静态块(如果有)中定义的值。在类中从上到下执行,在类层次结构中从上到下执行。
通常,有三种装载机:

  • 引导程序类加载器:每个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代码,展示类载入的子系统
    public class Test
    {
        public static void main(String[] args)
        {
            // string类被bootstrap载入器载入,bootstrap loader不是java的一个对象,所有是null
            System.out.println(String.class.getClassLoader());
            // 测试类,被Application载入器载入
            System.out.println(Test.class.getClassLoader());
        }
    }

    输出:

    null
    sun.misc.Launcher$AppClassLoader@73d16e93