jvm

JVM简介

[JVM] 简介java虚拟机jvm构成及原理

Posted by Hyuga on August 22, 2018

什么是JVM

引用百度百科的解释:

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

JVM运行原理

JVM是java的核心和基础,也是Java能跨平台的关键。

跨平台实现原理:

  • 不同平台有不同的Java虚拟机
  • java编译器只需要把源码编译成.class字节码文件,然后交由虚拟机将每一条指令翻译成不同平台的机器码,通过对应平台的JVM运行即可

JVM内存区域划分

  • 类装载器
  • 运行时数据区
  • 执行引擎

类装载器

Java类装载器(Java Classloader)是Java运行时环境(Java Runtime Environment)的一部分,负责动态加载Java类到Java虚拟机的内存空间中。

  • jvm有三个默认的类装载器,每上一层为当前类装载器的父类加载器
  • 双亲委派模式:jvm装载类时,每一层类加载器都会先请求父类装载器去装载,当父类装载器找不到,才会自己去装载类。为的是保证java核心类库不被破坏修改造成危害
  • java也支持自定义类装载器,必须继承ClassLoader,并重写loadClass和findClass方法,可打破双亲委派模式

运行时数据区

方法区

线程共享:方法区、堆

线程私有:Java栈、本地方法栈、程序计数寄存器

  • jvm装载.class文件,方法区存储类信息、常量、静态变量、类的字段和方法、类的访问权限、类名以及编译器编译后的代码等数据
  • jdk8之前,方法区是持久代,jvm中分配的内存空间
  • jdk8开始往后,方法区是元空间,元空间不在虚拟机中,而是使用本地内存
Java栈
  • 栈是jvm分配的一块内存,由栈帧组成,每当线程调用一个java方法时,JVM就会在该线程对应的栈中压入一个帧,而帧是由局部变量区操作数栈帧数据区组成
  • 栈是一种数据结构,后进先出
  • jvm对栈的操作有:以帧为单位的压栈或出栈
本地方法栈
  • 大致和Java栈一致,不过调用的方法是Native方法
PC寄存器(程序计数器)
  • 每条线程都对应一个独立的程序计数器,线程启动时创建
  • 记录当前线程执行的字节码的行号。
  • 一个很小的内存空间,是内存结构中唯一一个不会抛出OOM的内存区域

执行引擎

  • 执行引擎出于JVM的核心位置
  • 执行引擎执行.class字节码编译成机器码执行

Java代码执行流程

JVM类装载器装载编译后的.class字节码,并加载到运行时数据区,然后执行引擎会将这些字节码转换为机器码执行

java编译后生成的.class字节码,里面包含的是一堆jvm指令,执行引擎就是去解释执行这些指令
执行引擎运行的所有字节码指令只会操作活动线程中位于栈顶的栈帧

执行引擎将java字节码转换成机器码执行,有两种转方式:

  • 解释器执行:将编译好的.class字节码逐行地翻译为机器码执行
    • 优点:解释执行机制简单,逐行解释执行
    • 缺点:运行慢,当遇到循环代码,会进行多次重复翻译
  • 编译器执行[即时(Just-In-Time)编译器]:在适当时机把整段字节码编译成本地代码,当调用时没必要再去解释执行,可以直接通过本地代码执行
    • 优点:弥补解释器的不足,执行编译后的本地代码比逐行解释执行效率更高
    • 缺点:编译执行机制复杂,需要对方法内代码进行语法分析,优化等操作

JVM运行过程

还是引用百度百科上的举例:

public class HelloApp {
    public static void main(String[] args) {
        System.out.println("Hello World!");
        for (int i = 0; i < args.length; i++ ) {
            System.out.println(args);
        }
    }
}

javac HelloApp 生成HelloApp.class

java HelloApp run virtual machine 执行HelloApp的main,并入参

下面解析执行过程:

  1. JVM试图执行类HelloApp的main方法,发现该类并没有被装载,也就是说虚拟机当前不包含该类的二进制代码。
  2. 于是虚拟机通过类装载器ClassLoader试图寻找这个类的字节码文件。如果类装载失败,则抛出一个异常。
  3. 类被装载后同时在main方法被调用之前,必须对类HelloApp与其它类型进行链接然后初始化。
  • 类与其他类型进行链接,包含三个阶段
    • 检验:检查被装载的主类的符号和语义
    • 准备:创建类或接口的静态域以及把这些域初始化为标准的默认值
    • 解析:解析负责检查主类对其它类或接口的符号引用,在这一步它是可选的
  • 类的初始化
    • 类的初始化是对类中声明的静态初始化函数和静态域的初始化构造方法的执行
    • 一个类在初始化之前它的父类必须被初始化