java简介

Java虚拟机(jvm)

首先,我们来看看JVM,JDK,JRE之间的关系。

JDK(Java Development Kit);

           Java语言的软件开发工具包。它是物理的、真实存在的,它是programming tools、JRE、JVM的一个结合。

                                      java简介  

JRE(java Runtime Environment)

          java程序运行时的环境,JRE也是物理的、真实存在的。主要由JVM和Java API组成, 为Java程序执行的最低要求的环境。

                                                           java简介

JVM:

        1.定义:

        jvm是一种用于计算机设备的规范(一个抽象的规范),是一个虚拟的计算机软件的实现(一个具体的实现)。本质上是一个运行 byte code字节码的程序的一个容器。

       2.特点

         a.基于堆栈的虚拟机:目前世界上最流行的计算机体系结构都是都是基于寄存器,但是Java虚拟机确实基于栈结构的。

         b.符号引用:除了基本类型以外的数据类型都是通过符号来引用的,而不是通过地址来引用。

        c.垃圾回收:一个类的实例是由用户程序创建和垃圾回收自动销毁

        d.网络字节顺序:Java class文件用网络字节码顺序来存储,这样可以确保小端的Intel X86架构和大端的RISC系列架构之间               的无关性

 

      3.JVM的生命周期:

       JVM就是负责运行一个JAVA程序的;当启动一个JAVA程序时,Java虚拟机就开始运行,当一个Java程序运行结束时,JVM就消失了;如果同时运行多个Java程序,就会同时存在多个JVM。

      4.JVM的体系结构:

       Java代码的运行流程如下图:

                                          java简介

           a.类加载器(Class Loader)

           类加载器:负责加载程序的类型,例如类和接口,并且会赋予它们唯一的标识符作为它们的名字。

           一般情况下:JDK会默认提供三种类加载器:如下图所示

                                                             java简介

          1.BootStrap Classloader 是Java虚拟机通过执行main方法启动后初始化用的。

          2.BootStrap  Classload 负责架加载Extension Classloader,同时将Extension Classloader的父类加载器设置为BootStrap Classloader

          3..BootStrap  Classload在加载完Extension Classloader之后,就会加载Application Classloader,同时也会将Application Classloader的父类加载器设置为Extension Classloader

         

          Java中的类加载器(ClassLoader)在其加载过程中,采用了双亲委托机制,具体的加载步骤如下:

         1.当加载一个类时,ClassLoader 首先从自己已经加载的类(缓冲区)中查询是否该类已经被加载,如果该类已经被加载,就直接返回原来已经加载的类。

         2.如果ClassLoader当前的缓冲区没有找到已经被加载的这个类,它就是委托父类加载器去加载,父类加载器也会采取同样的策略。先查看父类自己的缓冲区,看看是否该类已经被加载,如果被加载了,就直接返回该类,如果没有被加载,然后就是委托父类的父类去加载该类,如此循环,一直到.BootStrap Classloader。

        3.如果所有的父类加载器都没有加载该类时,就由当前的ClassLoader 类加载器进行加载,同时将该类放在自己IDE缓冲区,以便下次有加载器请求时,直接返回。

       双亲委托加载器的核心思想就是:自底向上查询该类是否被加载;自顶向下尝试加载该类。

     类加载器的隔离问题:

          每一个类加载器都有自己的命名空间,此空间(缓冲区)用来保存已经加载过的类。当一个加载器加载一个类时,它就会保存在命名空间的类全局限定名进行收搜,查询该类是否已经被加载。

         JVM对类唯一的识别是ClassLoader+PackageName+ClassName,因此当运行一个Java程序时,有可能出现存在两个包名和类名完全相同的类,如果这个两个类不是由同一个ClassLoader加载的,那就不能将其中一个累的实例强制转换成另一个类的实例,这就叫做类加载器的隔离问题。

    

      类加载器的特点

       类加载器提供了动态加载的特性。在运行时的第一次引用到一个class的时候,就会对他进行加载、链接、初始化,而不是在编译时进行的。不同的JVM的实现不同,先表述的内容的均限于Hotspot JVM。

     层级结构:Java中的类加载器被组织成了有父子关系的层级结构,BootStrap Classloader类装载器是所有装载器的父类。

    代理模式:基于层级结构,类的代理可以在加载器之间进行代理。当加载器加载一个类时,首先会检查它的父类加载器是否对这个类进行了加载。如果父类加载器已经加载了这个类,这个类就会直接被使用,如果没有类加载器就会请求加载这个类。

   可见性限制:一个子加载器可以查找父类加载器的类,但是一个父类加载器无法查找子类加载器中的类。

   不允许卸载:类加载器可以加载一个类,但是不能卸载类,但是可以删除当前的类加载器,然后创建一个新的加载器来加载。

 

   类加载器的过程

      加载(loading):

             首先,根据类的全限定名找到类所在的Class文件,然后将其读取到一个字节数组中,随后,检验这些字节是否代表一个class对象,并且此class对象要包含正确的major、minor版本的信息。类的直接父类和接口也会被加载进来。当这些操作全部完成,类或者类的接口对象就从二进制表示中创建出来。

    链接(Linking)

           链接是检验类或者接口,并且准备类型和父类接口的过程,链接包含三个步骤:校验、准备、部分解析

         校验:这是类加载过程中最为复杂,最耗时的过程。它的主要职责是确保导入类型的准确性,校验阶段所做的检查,运行过程中就不需要再次做了,他虽然减慢的加载的速度,但是可以避免多次检查。

        准备:此过程通常分配了一个结构来存储类的信息,这个结构中包含了类中定义的成员变量,方法和接口信息

         部分解析:解析是可选择的阶段,它把类的常量池中的所有的符号引用改变成直接引用。如果不执行解析这个步骤,那么常量池中的符号解析要等到字节码指令使用到这个符号引用时才会进行解析。

    初始化(Initialization)

      把类中的变量初始化成合适的值。同时执行静态初始化程序,把静态变量初始化成制定的值。

 

   

   b.执行引擎(Execution Engine)

    通过类加载器加载的类,被分配到JVM的运行时数据区的字节码,会被执行引擎执行

      执行引擎是以指令为单位读取Java的字节码。它就像一个CPU一样,一条一条的执行字节码指令。每个字节码指令都是由一个1字节的操作码加上附加的操作数组成。执行引擎就是一个操作码,它根据操作数来执行任务,任务完成后,继续执行下一个操作码。

     Java字节码是一种人类可以读懂的语言编写的,但是计算机不能直接执行它。故需要靠执行引擎必须把字节码转换成可以被JVM直接执行的语言。

    字节码可以通过以下两种方式转成成机器语言

      解释器: 解释器一条一条的读取字节码解释并且执行字节码指令。因为它是一条一条解释和执行字节码指令,所以它可以很快的解释字节码,但是执行起来会比较慢,这个是解释执行的语言的一个缺点。字节码这种语言基本来说是解释执行的。

      即时编译器:它的引用是用来弥补解释器的缺点的。执行引擎首先按照解释执行的方式来执行,然后在合适的时候,及时编译器整段字节码编译成本地代码。然后,执行引擎就没有必要再去解释执行指令了,它可以直接通过本地代码去执行它。执行本地代码比一条一条的进行解释执行的速度要快的多。编译后的代码可以执行的很快,因为本地代码是保存在缓存中的。

  

      Java字节码是要解释执行的,但是没有直接在JVM执行原生代码块。为了提高性能,Oracle Hotspot虚拟机将会找到执行最频繁的字节码片段并且把他们编译成原生机器码。编译出的原生机器码将储存的非堆内存的代码缓存中。