java 虚拟机原理

2020-01-18  本文已影响0人  柬埔没有寨

什么是JVM

JVM是Java Virtual Machine(Java虚拟机)的缩写,是一个虚构出来的计算机,它屏蔽了与具体操作系统平台相关的信息,使得Java程序只需生成在Java虚拟机上运行的目标代码(字节码,ByteCode), 就可以在多种平台上不加修改地运行。这背后其实就是JVM把字节码翻译成具体平台上的机器指令,从而实现“一次编写,到处运行(Write Once, Run Anywhere)”。
Java为什么能够跨平台?
Java引入了字节码的概念,jvm 只能认识字节码,并将它们解释到系统的API调用。针对不同的系统有不同的jvm实现,有 Linux 版本的 jvm 实现,也有 Windows 版本的 jvm 实现,但是同一段代码在编译后的字节码是一样的。在不同的系统平台上运行是通过JAVA解释器将字节码解释为不同平台的机器码,在不同的 jvm 实现上会映射到不同系统的 API 调用,从而实现代码的不加修改即可跨平台运行。

JVM、JRE、JDK的关系

  1. JRE(Java Runtime Environment,Java运行环境),面向Java程序的使用者,而不是开发者。JRE是运行Java程序所必须环境的集合,包含JVM标准实现及 Java核心类库。它包括Java虚拟机、Java平台核心类和支持文件
  2. JDK(Java Development Kit,Java开发工具包),包括了Java运行环境(JRE),并提供了一堆Java工具tools.jar和Java标准类库 (rt.jar)

三者的关系是:JDK>JRE>JVM

java虚拟机运行原理

按照阶段分为两个阶段:
编译阶段:当我们将一个.java的文件进行编译,编译程序会生成一个相同名字而后缀为.class的文件。
运行阶段主要分为以下步骤:

JVM加载流程.jpg
  1. 加载

    • 通过一个类的全限定名来获取该类的二进制字节流
    • 将这个字节流的静态存储结构转化为方法区运行时数据结构
    • 在内存堆中生成一个代表该类的java.lang.Class对象,作为该类数据的访问入口
  2. 验证
    验证、准备、解析这三步可以看做是一个连接的过程,将类的字节码连接到JVM的运行状态之中
    验证是为了确保Class文件的字节流中包含的信息符合当前虚拟机的要求,不会威胁到jvm的安全,主要包括以下几个方面的验证:

    • 文件格式的验证,验证字节流是否符合Class文件的规范,是否能被当前版本的虚拟机处理
    • 元数据验证,对字节码描述的信息进行语义分析,确保符合java语言规范
    • 字节码验证 通过数据流和控制流分析,确定语义是合法的,符合逻辑的
    • 符号引用验证 这个校验在解析阶段发生
  3. 准备
    为类的静态变量分配内存,初始化为系统的初始值。对于final static修饰的变量,直接赋值为用户的定义值。如下面的例子:这里在准备阶段过后的初始值为0,而不是7

    public static int a=7
    
  4. 解析

    解析是将常量池内的符号引用转为直接引用(如物理内存地址指针)

  5. 初始化

    到了初始化阶段,jvm才真正开始执行类中定义的java代码
    1)初始化阶段是执行类构造器<clinit>()方法的过程。类构造器<clinit>()方法是由编译器自动收集类中的所有类变量的赋值动作和静态语句块(static块)中的语句合并产生的。
    2)当初始化一个类的时候,如果发现其父类还没有进行过初始化、则需要先触发其父类的初始化。
    3)虚拟机会保证一个类的<clinit>()方法在多线程环境中被正确加锁和同步。

JVM内存分区

JAVA内存分区.png
  1. 程序计数器

    程序计数器(Progarm Counter Register)是一块较小的内存空间,它可以看作是当前线程所执行的字节码行号指示器。在JVM中,通过程序计数器来记录某个线程的字节码执行位置,或者说记录下一条要运行的指令。程序计数器是具备线程隔离的特性,也就是说,每个线程工作时都有属于自己的独立计数器,互不影响,是一块线程私有的内存空间。
    如果当前正在执行的是一个java方法,程序计数器会记录正在执行的java字节码地址;如果正在执行的是native方法,则程序计数器为空。

  2. Java虚拟机栈

    java虚拟机栈是线程私有的内存空间,它用来保存方法的局部变量、部分结果,并参与方法的调用和返回。


    栈帧结构.png

    虚拟机栈在运营师采用栈帧来保存数据,栈帧中主要有局部变量表、操作数栈、动态链接地址、返回地址等信息。每一个方法的调用都伴随着栈帧的入栈操作,相应的,方法的返回则对应着栈帧的出战操作。

    和java栈相关的两个异常:

    1. StackOverFlowError
      在线程的计算过程中,如果请求的栈的深度大于最大可用的栈深度,则抛出改异常。
      
    2. OutOfMemoryError
      如果java的栈可以扩展,在程序运行过程中,没有足够的内存来支撑程序的扩展,则抛出该异常。
      
  3. 本地方法栈
    本地方法栈和java虚拟机栈功能类似,本地方法栈主要管理本地方法栈的调用,一般是指有C实现的。和java虚拟机栈一样会抛出StackOverFlowError和OutOfMemoryError异常

  4. 方法区

    方法区是java内存区域中比较重要的一部分,主要保存的信息是元数据。其中最为重要的是类的类型信息、常量池、域信息、方法信息。

  5. Java堆
    Java堆可以说是Java运行时内存中最为重要的一部分,几乎所有的对象和数据都是在堆中分配空间的。Java堆分为新生代和老年代两个部分,新生代用于存放刚刚产生的对象,如果对象一直没有被回收,生存的足够长,老年对象就会被移入老年代。
    新生代又可以细分为eden、surivor space0(s0或者from space)和surivor space1(s1或者To space)。eden存放刚刚创建的对象,s0和s1存放的对象至少经历了一次垃圾回收,等幸存下来。如果幸存去的对象到了指定年龄仍未被回收,就会进入老年代。
    持久代:Permanent Generation。在Sun的JVM中就是方法区的意思,尽管有些JVM大多没有这一代。主要存放常量及类的一些信息默认最小值为16MB,最大值为64MB


    堆内存结构.jpg

垃圾收集算法

垃圾收集器

上一篇 下一篇

猜你喜欢

热点阅读