JVM原理和调优

前言


首先表明一下文章的出处:JVM原理讲解和调优,基于该文之上我又做一了些特别的说明和解释用于自我学习和理解。

JVM原理

  • 什么是JVM
    JVM是Java Virtual Machine(Java虚拟机)的缩写,JVM是一种用于计算设备的规范,它是一个虚构出来的计算机,是通过在实际的计算机上仿真模拟各种计算机功能来实现的。

    Java语言的一个非常重要的特点就是与平台的无关性。而使用Java虚拟机是实现这一特点的关键。一般的高级语言如果要在不同的平台上运行,至少需要编译成不同的目标代码。而引入Java语言虚拟机后,Java语言在不同平台上运行时不需要重新编译。Java语言使用Java虚拟机屏蔽了与具体平台相关的信息,使得Java语言编译程序只需生成在Java虚拟机上运行的目标代码(字节码),就可以在多种平台上不加修改地运行。Java虚拟机在执行字节码时,把字节码解释成具体平台上的机器指令执行。这就是Java的能够“一次编译,到处运行”的原因。

    从Java平台的逻辑结构上来看,我们可以从下图来了解JVM:
    这里写图片描述
    从上图能清晰看到Java平台包含的各个逻辑模块,也能了解到JDK与JRE的区别,对于JVM自身的物理结构,我们可以从下图鸟瞰一下:
    这里写图片描述
    这里写图片描述
    下面我们将从JAVA代码编译和执行过程来了解JVM在这一整个过程中都做了什么。

  • JAVA代码编译和执行过程

    • Java代码编译是由Java源码编译器来完成,流程图如下所示:
      这里写图片描述

    • Java字节码的执行是由JVM执行引擎来完成,流程图如下所示:
      这里写图片描述

  • Java代码编译和执行的整个过程包含了以下三个重要的机制:

    • Java源码编译机制

      • 分析和输入到符号表
      • 注解处理
      • 语义分析和生成class文件

      • 流程图如下所示:
        这里写图片描述

      • 最后生成的class文件由以下部分组成:
        • 结构信息。包括class文件格式版本号及各部分的数量与大小的信息
        • 元数据。对应于Java源码中声明与常量的信息。包含类/继承的超类/实现的接口的声明信息、域与方法声明信息和常量池
        • 方法信息。对应Java源码中语句和表达式对应的信息。包含字节码、异常处理器表、求值栈与局部变量区大小、求值栈的类型记录、调试符号信息
    • 类加载机制
      JVM的类加载是通过ClassLoader及其子类来完成的,类的层次关系和加载顺序可以由下图来描述:
      这里写图片描述

      • Bootstrap ClassLoader
        负责加载$JAVA_HOME中jre/lib/rt.jar里所有的class,由C++实现,不是ClassLoader子类

      • Extension ClassLoader
        负责加载java平台中扩展功能的一些jar包,包括$JAVA_HOME中jre/lib/*.jar或-Djava.ext.dirs指定目录下的jar包

      • App ClassLoader
        负责记载classpath中指定的jar包及目录中class
      • Custom ClassLoader
        属于应用程序根据自身需要自定义的ClassLoader,如tomcat、jboss都会根据j2ee规范自行实现ClassLoader加载过程中会先检查类是否被已加载,检查顺序是自底向上,从Custom ClassLoader到BootStrap ClassLoader逐层检查,只要某个classloader已加载就视为已加载此类,保证此类只所有ClassLoader加载一次。而加载的顺序是自顶向下,也就是由上层来逐层尝试加载此类。
      • 类加载器的加载顺序
        这里写图片描述
        JVM并不是把所有的类一次性全部加载到JVM中的,也不是每次用到一个类的时候都去查找,对于JVM级别的类加载器在启动时就会把默认的JAVA_HOME/lib里的class文件加载到JVM中,因为这些是系统常用的类,对于其他的第三方类,则采用用到时就去找,找到了就缓存起来的,下次再用到这个类的时候就可以直接用缓存起来的类对象了。
    • 类执行机制
      JVM是基于栈的体系结构来执行class字节码的。线程创建后,都会产生程序计数器(PC)和栈(Stack),程序计数器存放下一条要执行的指令在方法内的偏移量,栈中存放一个个栈帧,每个栈帧对应着每个方法的每次调用,而栈帧又是有局部变量区和操作数栈两部分组成,局部变量区用于存放方法中的局部变量和参数,操作数栈中用于存放方法执行过程中产生的中间结果。栈的结构如下图所示:
      这里写图片描述

      • 一个例子:
        当线程调用Java方法时,虚拟机会创建一个新的栈帧并压入Java栈。然而当它调用的是本地方法时,虚拟机会保持Java栈不变,不再在线程的Java栈中压入新的帧,虚拟机只是简单地动态连接并直接调用指定的本地方法。

        • JAVA方法 VS 本地方法

          • JAVA方法是由JAVA编写的,编译成字节码,存储在class文件中。
          • 本地方法是由其它语言编写的,编译成和处理器相关的机器代码本地方法保存在动态链接库中,即.dll(windows系统)文件中,格式是各个平台专有的。

猜你喜欢

转载自blog.csdn.net/u012437781/article/details/80082292