Java虚拟机架构

时间:2022-12-27 12:43:12

                                                           Java虚拟机架构

     一、概述

            Java 虚拟机是物理机的软件实现。Java的设计理念是WORAWrite Once Run Anywhere,一次编写随处运行)

    一个运行中的Java虚拟机有着一个清晰的任务:执行Java程序。程序开始执行时他才运行,程序结束时他就停止。你在同一台机器上运行三个程序,就会有三个运行中的Java虚拟机。Java虚拟机总是开始于一个main()方法,这个方法必须是公有、返回void、直接受一个字符串数组。

    Main()方法是程序的起点,他被执行的线程初始化为程序的初始线程。程序中其他的线程都由他来启动。Java中的线程分为两种:守护线程 (daemon)和普通线程(non-daemon)。守护线程是Java虚拟机自己使用的线程,比如负责垃圾收集的线程就是一个守护线程。包含Main()方法的初始线程不是守护线程。 只要Java虚拟机中还有普通的线程在执行,Java虚拟机就不会停止。

 二、JVM架构

        Java虚拟机架构

 从上图中可以看出:JVM包含三部分,分别是:类加载器、执行引擎和运行时数据区

 一个java源文件即(.java)文件是如何被执行的。 首先,(.java)文件通过java编译器编译成java字节码文件即(.class);然后,类加载器将硬盘上的类的.class文件中的二进制数据读入到内存中,将其放在运行时数据区的方法区里面,最后执行引擎执行运行时数据区的方法区里面的字节码文件

 1.类加载器

   类加载器将硬盘上的类的.class文件中的二进制数据读入到内存中,将其放在运行时数据区的方法区里面。但是它不负责这个类文件能否执行,而这个是执行引擎负责的

 2.执行引擎

  分配给运行时数据区中的方法区的字节码将由执行引擎执行,执行引擎读取字节码并逐个执行。此外,执行引擎还可执行本地方法

 3.运行时数据区

  运行时数据区包含5大部分,分别是方法区、堆、pc寄存器、jvm栈和本地方法栈

  1)方法区   

  方法区(Method Area,是各个线程共享的内存区域,它用于存储已被虚拟机加载的类信息、常量、静态变量、即时编译器编译后的代码等数据。当方法区无法满足内存分配需求时,将抛出OutOfMemoryError异常。

  运行时常量池(Runtime Constant Pool)是方法区的一部分。Class文件中除了有类的版本、字段、方法、接口等描述等信息外,还有一项信息是常量池(Constant Pool Table),用于存放编译期生成的各种字面量和符号引用,这部分内容将在类加载后存放到方法区的运行时常量池中。

  2)堆 

  Java 堆(Java Heap)是Java虚拟机所管理的内存中最大的一块。Java堆是被所有线程共享的一块内存区域,在虚拟机启动时创建。此内存区域的唯一目的就是存放对象实例,几乎所有的对象实例都在这里分配内存

 Java 堆是垃圾收集器管理的主要区域.如果从内存回收的角度看,由于现在收集器基本都是采用的分代收集算法,所以Java堆中还可以细分为:新生代和老年代;再细致一点的有Eden空间、From Survivor空间、To Survivor空间等。

 如果在堆中没有内存完成实例分配,并且堆也无法再扩展时,将会抛出OutOfMemoryError异常。

 3)pc寄存器

  pc寄存器是一块较小的内存空间,它的作用可以看做是当前线程所执行的字节码的行号指示器。字节码解释器工作时就是通过改变这个计数器的值来选取下一条需要执行的字节码指令。

 为了线程切换后能恢复到正确的执行位置,每条线程都需要有一个独立的程序计数器,各条线程之间的计数器互不影响,独立存储,我们称这类内存区域为“线程私有”的内存。如果线程正在执行的是一个Java 方法,这个计数器记录的是正在执行的虚拟机字节码指令的地址;如果正在执行的是Natvie方法,这个计数器值则为空(Undefined)。此内存区域是唯一一个在Java虚拟机规范中没有规定任何OutOfMemoryError 情况的区域。

 4)jvm栈

 与pc寄存器一样,Java 虚拟机栈(Java Virtual Machine Stacks)也是线程私有的,它的生命周期与线程相同。虚拟机栈描述的是Java方法执行的内存模型:每个方法被执行的时候都会同时创建一个栈帧(Stack Frame)用于存储局部变量表、操作栈、动态链接、方法出口等信息。每一个方法被调用直至执行完成的过程,就对应着一个栈帧在虚拟机栈中从入栈到出栈的过程。

 Java 虚拟机规范中,对这个区域规定了两种异常状况:如果线程请求的栈深度大于虚拟机所允许的深度,将抛出*Error异常;如果虚拟机栈可以动态扩展(当前大部分的Java虚拟机都可动态扩展,只不过Java虚拟机规范中也允许固定长度的虚拟机栈),当扩展时无法申请到足够的内存时会抛出OutOfMemoryError异常。

 5)本地方法栈

  虚拟机栈为虚拟机执行Java 方法(也就是字节码)服务,而本地方法栈则是为虚拟机使用到的Native 方法服务。与虚拟机栈一样,本地方法栈区域也会抛出*Error 和OutOfMemoryError异常。

 三、小知识点

  1.JVM参数的含义

 -Xms   初始堆大小

 -Xmx   最大堆大小

 -Xmn  年轻代大小

 -XX:NewRatio 年轻代(包括Eden和两个Survivor)与年老代的比值(除去持久代),-XX:NewRatio=4表示年轻代与年老代所占比值为1:4,年轻代占整个堆栈的1/5

 -XX:SurvivorRatio Eden区与Survivor区的大小比值设置为8,则两个Survivor区与一个Eden区的比值为2:8,一个Survivor区占整个年轻代的1/10

    2.堆和栈的区别

      1)栈内存用来存储局部变量和方法调用信息。而堆内存用来存储Java中的对象。无论是成员变量、局部变量还是类变量,它们指向的对象都存储在堆内存中。

      2)栈的内存要远远小于堆内存,如果你使用递归的话,那么你的栈很快就会充满并产生*Error。

      3)栈内存归属于线程的私有内存,每个线程都会有一个栈内存,其存储的变量只能在其所属线程中可见。而堆内存中的对象对所有线程可见,可以被所有线程访问。

      4)如果线程请求的栈深度大于虚拟机所允许的深度,将抛出*Error异常。如果JVM栈可以动态扩展(大部分JVM是可以的),当扩展时无法申请到足够内存则抛出OutOfMemoryError异常。
     而堆内存没有可用的空间存储生成的对象,JVM会抛出java.lang.OutOfMemoryError。