JVM学习总结--JVM简介

JVM是什么

JVM全称是Java Virtual Machine(Java虚拟机)。它之所以被称之为是“虚拟”的,就是因为它仅仅是由一个规范来定义的抽象计算机。我们平时经常使用的Sun HotSpot虚拟机只是其中一个具体的实现(另外还有BEA JRockit、IBM J9等等虚拟机)。

JVM的设计目标是提供一个基于抽象规格描述的计算机模型,为解释程序开发人员提供很好的灵活性,同时也确保Java代码可在符合该规范的任何系统上运行。JVM对其实现的某些方面给出了具体的定义,特别是对Java可执行代码,即字节码(Bytecode)的格式给出了明确的规格。这一规格包括操作码和操作数的语法和数值、标识符的数值表示方式、以及Java类文件中的Java对象、常量缓冲池在JVM的存储映象。这些定义为JVM解释器开发人员提供了所需的信息和开发环境。Java的设计者希望给开发人员以随心所欲使用Java的自由。

JVM是java的核心和基础,在java编译器和os平台之间的虚拟处理器。它是一种基于下层的操作系统和硬件平台并利用软件方法来实现的抽象的计算机,可以在上面执行java的字节码程序。

JRE/JDK/JVM是什么关系

JRE(JavaRuntimeEnvironment,Java运行环境),也就是Java平台。所有的Java 程序都要在JRE下才能运行。普通用户只需要运行已开发好的java程序,安装JRE即可。

JDK(Java Development Kit)是程序开发者用来来编译、调试java程序用的开发工具包。JDK的工具也是Java程序,也需要JRE才能运行。为了保持JDK的独立性和完整性,在JDK的安装过程中,JRE也是安装的一部分。所以,在JDK的安装目录下有一个名为jre的目录,用于存放JRE文件。

JVM(JavaVirtualMachine,Java虚拟机)是JRE的一部分。它是一个虚构出来的计算机,是通过在实际的计算机上仿真模拟各种计算机功能来实现的。JVM有自己完善的硬件架构,如处理器、堆栈、寄存器等,还具有相应的指令系统。Java语言最重要的特点就是跨平台运行。使用JVM就是为了支持与操作系统无关,实现跨平台。


JVM的生命周期

当启动一个Java程序时,一个虚拟机实例也就诞生了。当该程序关闭退出,这个虚拟机实例也就随之消亡。如果在同一台计算机上同时运行三个Java程序,将得到三个Java虚拟机实例。每个Java程序都运行于它自己的Java虚拟机实例中。

JVM实例对应了一个独立运行的java程序,它是进程级别。

1、启动。

启动一个Java程序时,一个JVM实例就产生了,任何一个拥有publicstatic void main(String[] args)函数的class都可以作为JVM实例运行的起点

2、运行。

main()作为该程序初始线程的起点,任何其他线程均由该线程启动。JVM内部有两种线程:守护线程和非守护线程,main()属于非守护线程,守护线程通常由JVM自己使用,java程序也可以标明自己创建的线程是守护线程

3、消亡。

当程序中的所有非守护线程都终止时,JVM才退出;若安全管理器允许,程序也可以使用Runtime类或者System.exit()来退出。

JVM运行原理

操作系统装入JVM是通过jdk中Java.exe来完成,通过下面4步来完成JVM环境。

 

1、JVM装入环境。

JVM提供的方式是操作系统的动态连接文件。既然是文件那就存在一个装入路径的问题,Java是怎么找这个路径的呢?下面基于Windows的实现的分析。

首先查找jre路径,Java是通过GetApplicationHomeapi来获得当前的Java.exe绝对路径,c:\jdk1.7.0_45\bin\Java.exe,然后截取到绝对路径c:\jdk1.7.0_45\,判断c:\jdk1.7.0_45\bin\Java.dll文件是否存在,如果存在就把c:\jdk1.7.0_45\作为jre路径,如果不存在则判断c:\jdk1.7.0_45\jre\bin\Java.dll是否存在,如果存在这c:\jdk1.7.0_45\jre作为jre路径,如果不存在调用GetPublicJREHome查HKEY_LOCAL_MACHINE\Software\JavaSoft\JavaRuntime Environment\“当前JRE版本号”\JavaHome的路径为jre路径。

然后装载JVM.cfg文件。在我们的jdk目录中jre\bin\server和jre\bin\client都有JVM.dll文件存在,而Java正是通过JVM.cfg配置文件来管理这些不同版本的JVM.dll的。

最后获得JVM.dll的路径,JRE路径+\bin+\JVM类型字符串+\JVM.dll就是JVM的文件路径了,但是如果在调用Java程序时用-XXaltJVM=参数指定的路径path,就直接用path+\JVM.dll文件做为JVM.dll的文件路径。

 

2、装载JVM.dll

通过第一步已经找到了JVM的路径,Java通过LoadJavaVM来装入JVM.dll文件。装入工作很简单,就是调用Windows API函数:

LoadLibrary装载JVM.dll动态连接库.然后把JVM.dll中的导出函数JNI_CreateJavaVM和JNI_GetDefaultJavaVMInitArgs挂接到InvocationFunctions变量的CreateJavaVM和GetDefaultJavaVMInitArgs函数指针变量上。JVM.dll的装载工作宣告完成。

 

3、初始化JVM。

挂接到JNIENV(JNI调用接口)实例,获得本地调用接口,这样就可以在Java中调用JVM的函数了。调用InvocationFunctions->CreateJavaVM也就是JVM中JNI_CreateJavaVM方法获得JNIEnv结构的实例。

 

4、运行Java程序.

Java程序有两种方式一种是jar包,一种是class。运行jar(Java -jarXXX.jar)的时候,Java.exe调用GetMainClassName函数,该函数先获得JNIEnv实例然后调用Java类Java.util.jar.JarFileJNIEnv中方法getManifest()并从返回的Manifest对象中取getAttributes("Main-Class")的值即jar包中文件:META-INF/MANIFEST.MF指定的Main-Class的主类名作为运行的主类。之后main函数会调用Java.c中LoadClass方法装载该主类(使用JNIEnv实例的FindClass)。main函数直接调用Java.c中LoadClass方法装载该类。如果是执行class方法。main函数直接调用Java.c中LoadClass方法装载该类。

然后main函数调用JNIEnv实例的GetStaticMethodID方法查找装载的class主类中“publicstatic void main(String[] args)”方法,并判断该方法是否为public方法,然后调用JNIEnv实例的CallStaticVoidMethod方法调用该Java类的main方法。

JVM体系结构

JVM的基本组成
(1)指令集:JVM指令集
(2)类加载器:在jvm启动时或者类在运行时将需要的class加载到JVM中
(3)执行引擎:负责执行class文件中的字节码指令,相当于CPU
(4)运行时数据区:将内存划分成若干个区,分别完成不同的任务
(5)本地方法区:调用C或C++实现的本地方法代码返回的结果

  1. 类加载器ClassLoader

    会在下一篇文章中详细说明ClassLoader加载机制。每一个被JVM装载的类型都有一个与之对应的Java.lang.Class类的实例来表示该类型。该实例可以唯一表示被jvm装载的class类,这个实例和其他类的实例一样放在堆内存中。

       2.执行引擎
        执行引擎相当于线程,是JVM的核心,执行引擎的作用就是解析JVM字节码指令,得到执行的结果。执行引擎由各个厂家实现。SUN的hotspot是一种基于栈的执行引擎。执行引擎也就是执行一条条代码的一个流程,代码都包含在方法体中,执行引擎本质上就是执行一个个方法串起来的流程,对应于操作系统的一个线程,每个java线程就是一个执行引擎的实例。

      

       3.java内存管理
          

          执行引擎在执行的过程中需要存储一些东西,如操作数,操作码执行结果,class类的字节码以及类的对象等信息都需要在执行引擎执行前准备就绪。JVM有一个方法区,java堆区,java栈,PC寄存器和本地方法区。其中方法区和java堆是线程共享的。如果当前线程对应的java栈中没有栈帧,这个java栈也要被JVM撤销,整个JVM退出。

JVM选择基于栈的架构的原因


  JVM执行字节码指令是基于栈的架构的,所有的操作数必须先入栈,然后根据指令的操作码选择从栈顶弹出若干个元素进行计算后再将结果入栈。JVM操作数可以存放在每一个栈帧中的一个本地变量中,即每个方法调用时就会给这个方法分配一个本地变量集,这个本地变量集在编译时就已经确定,所以操作数入栈可以直接是常量或者从本地变量集中娶一个变量压入栈中。
  JVM基于栈的设计理由是
  (1)JVM要设计成与平台无关的,而平台无关性就要保证在没有或者由很少的寄存器的机器上也能同样正确执行java代码,因为寄存器很难做到通用。
  (2)基于栈的理由是为JVM更好地优化代码而设计的
  (3)为了指令的紧凑性,因为java代码可能在网络上传输,所以class文件的大小也是设计JVM字节码指令的一个重要因素。


ps: 本文很多是对《深入理解java虚拟机》的总结, 各位如果还有哪里不明白的,或是我这里讲的还不够透彻,亦或是讲错了的地方请留言指正,让我们共同进步,谢谢!


  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值