在我们运行和调试Java程序的时候,经常会提到一个JVM的概念.那JVM到底是什么呢?

JVM是Java程序的运行环境,它同时也是一个操作系统的一个应用程序、一个进程,因此他也有他自己的运行生命周期,也有自己的代码和数据空间.JVM包括一套字节码指令集、一组寄存器、一个栈、一个垃圾回收堆和一个存储方法域。

首先来说一下JVM工作原理中的jdk这个东西,不管你是初学者还是高手,是j2ee程序员还是j2se程序员,jdk总是在帮我们做一些事情.我 们在了解Java之前首先大师们会给我们提供说jdk这个东西.它在Java整个体系中充当着什么角色呢?我很惊叹sun大师们设计天才,能把一个如此完 整的体系结构化的如此完美.jdk在这个体系中充当一个生产加工中心,产生所有的数据输出,是所有指令和战略的执行中心.本身它提供了Java的完整方 案,可以开发目前Java能支持的所有应用和系统程序.这里说一个问题,大家会问,那为什么还有j2me,j2ee这些东西,这两个东西目的很简单,分别 用来简化各自领域内的开发和构建过程.jdk除了JVM之外,还有一些核心的API,集成API,用户工具,开发技术,开发工具和API等组成

好了,废话说了那么多,来点于主题相关的东西吧.JVM在整个jdk中处于最底层,负责于操作系统的交互,用来屏蔽操作系统环境,提供一个完整的Java运行环境,因此也就是虚拟计算机. 操作系统装入JVM是通过jdk中Java.exe来完成,通过下面4步来完成JVM环境.

1.创建JVM装载环境和配置

2.装载JVM.dll

3.初始化JVM.dll并挂界到JNIENV(JNI调用接口)实例

4.调用JNIEnv实例装载并处理class类。

一.JVM装入环境,JVM提供的方式是操作系统的动态连接文件.既然是文件那就一个装入路径的问题,Java是怎么找这个路径的呢?当你在调用Java test的时候,操作系统会在path下找到你的Java.exe程序,Java.exe就通过下面一个过程来确定JVM的路径和相关的参数配置.下面基于Windows的实现的分析.

首先查找jre路径:Java是 通过GetApplicationHome api来获得当前的Java.exe绝对路径,c:\j2sdk1.4.2_09\bin\Java.exe,那么它会截取到绝对路径c: \j2sdk1.4.2_09\,判断c:\j2sdk1.4.2_09\bin\Java.dll文件是否存在,如果存在就把c: \j2sdk1.4.2_09\作为jre路径,如果不存在则判断c:\j2sdk1.4.2_09\jre\bin\Java.dll是否存在,如果存 在这c:\j2sdk1.4.2_09\jre作为jre路径.如果不存在就会接着调用GetPublicJREHome查 HKEY_LOCAL_MACHINE\Software\JavaSoft\Java Runtime Environment\“当前JRE版本号”\JavaHome的路径为jre路径。

然后装载JVM.cfg文件:JRE路径+\lib+\ARCH(CPU构架)+\JVM.cfg。 ARCH(CPU构架)的判断是通过Java_md.c中的GetArch函数判断的,该函数在windows平台只有两种情况:WIN64的 ‘ia64’,其他情况都为‘i386’。以我的为例:C:\j2sdk1.4.2_09\jre\lib\i386\JVM.cfg.主要的内容如下:

-client KNOWN

-server KNOWN

-hotspot ALIASED_TO -client

-classic WARN

-native ERROR

-green ERROR

在我们的jdk目录中jre\bin\server和jre\bin\client都有JVM.dll文件存在,而Java正是通过JVM.cfg配置文件来管理这些不同版本的JVM.dll的.通过文件我们可以定义目前jdk中支持那些JVM,前面部分(client)是JVM名称,后面是参数,KNOWN表示JVM存在,ALIASED_TO表示给别的JVM取一个别名,WARN表示不存在时找一个JVM替代,ERROR表示不存在抛出异常. 在运行Java XXX时,Java.exe会通过CheckJVMType来检查当前的JVM类型,Java可以通过两种参数的方式来指定具体的JVM类型,一种按照 JVM.cfg文件中的JVM名称指定,第二种方法是直接指定,它们执行的方法分别是“Java -J”、“Java -XXaltJVM=”或“Java -J-XXaltJVM=”。如果是第一种参数传递方式,CheckJVMType函数会取参数‘-J’后面的JVM名称,然后从已知的JVM配置参数中 查找如果找到同名的则去掉该JVM名称前的‘-’直接返回该值;而第二种方法,会直接返回“-XXaltJVM=”或“-J-XXaltJVM=”后面的 JVM类型名称;如果在运行Java时未指定上面两种方法中的任一一种参数,CheckJVMType会取配置文件中第一个配置中的JVM名称,去掉名称 前面的‘-’返回该值。CheckJVMType函数的这个返回值会在下面的函数中汇同jre路径组合成JVM.dll的绝对路径。如果没有指定这会使用 JVM.cfg中第一个定义的JVM.可以通过set _Java_LAUNCHER_DEBUG=1在控制台上测试.

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

二:装载JVM.dll

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

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

三:初始化JVM,获得本地调用接口,这样就可以在Java中调用JVM的函数了.调用InvocationFunctions->CreateJavaVM也就是JVM中JNI_CreateJavaVM方法获得JNIEnv结构的实例.

四:运行Java程序.

Java程序有两种方式一种是jar包,一种是class. 运行jar,Java -jar XXX.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主类中

“public static void main(String[] args)”方法,并判断该方法是否为public方法,然后调用JNIEnv实例的

CallStaticVoidMethod方法调用该Java类的main方法。

a:指令系统

JVM指令系统同其他计算机的指令系统极其相似。Java指令也是由操作码和操作数两部分组成。操作码为8位二进制数,操作数紧随在操作码的后面,其长度 根据需要而不同。操作码用于指定一条指令操作的性质(在这里我们采用汇编符号的形式进行说明),如iload表示从存储器中装入一个整 数,anewarray表示为一个新数组分配空间,iand表示两个整数的"与",ret用于流程控制,表示从对某一方法的调用中返回。当长度大于8位 时,操作数被分为两个以上字节存放。JVM采用了"big endian[3]"的编码方式来处理这种情况,即高位bits存放在低字节中。这同 Motorola及其他的RISC CPU采用的编码方式是一致的,而与Intel采用的"little endian "的编码方式即低位bits存放在低位字节的方法不同。Java指令系统是以Java语言的实现为目的设计的,其中包含了用于调用方法和监视多线程系统的 指令。Java的8位操作码的长度使得JVM最多有256种指令,已使用了160多种操作码。

b:寄存器

所有的CPU均包含用于保存系统状态和处理器所需信息的寄存器组。如果虚拟机定义较多的寄存器,便可以从中得到更多的信息而不必对栈或内存进行访 问,这有利于提高运行速度。然而,如果虚拟机中的寄存器比实际CPU的寄存器多,在实现虚拟机时就会占用处理器大量的时间来用常规存储器模拟寄存器,这反 而会降低虚拟机的效率。针对这种情况,JVM只设置了4个最为常用的寄存器。它们是:

pc程序计数器

optop操作数栈顶指针

frame当前执行环境指针

vars指向当前执行环境中第一个局部变量的指针

所有寄存器均为32位。pc用于记录程序的执行。optop,frame和vars用于记录指向Java栈区的指针。

c:栈结构

作为基于栈结构的计算机,Java栈是JVM存储信息的主要方法。当JVM得到一个Java字节码应用程序后,便为该代码中一个类的每一个方法创建一个栈框架,以保存该方法的状态信息。每个栈框架包括以下三类信息:

局部变量

执行环境

操作数栈

局部变量用于存储一个类的方法中所用到的局部变量。vars寄存器指向该变量表中的第一个局部变量。

执行环境用于保存解释器对Java字节码进行解释过程中所需的信息。它们是:上次调用的方法、局部变量指针和操作数栈的栈顶和栈底指针。执行环境是 一个执行一个方法的控制中心。例如:如果解释器要执行iadd(整数加法),首先要从frame寄存器中找到当前执行环境,而后便从执行环境中找到操作数 栈,从栈顶弹出两个整数进行加法运算,最后将结果压入栈顶。

操作数栈用于存储运算所需操作数及运算的结果。

d:碎片回收

Java类的实例所需的存储空间是在堆上分配的。解释器具体承担为类实例分配空间的工作。解释器在为一个实例分配完存储空间后,便开始记录对该实例所占用 的内存区域的使用。一旦对象使用完毕,便将其回收到堆中。在Java语言中,除了new语句外没有其他方法为一对象申请和释放内存。对内存进行释放和回收 的工作是由Java运行系统承担的。这允许Java运行系统的设计者自己决定碎片回收的方法。在SUN公司开发的Java解释器和Hot Java环境中,碎片回收用后台线程的方式来执行。这不但为运行系统提供了良好的性能,而且使程序设计人员摆脱了自己控制内存使用的风险。

e:存储区

JVM有两类存储区:常量缓冲池和方法区。常量缓冲池用于存储类名称、方法和字段名称以及串常量。方法区则用于存储Java方法的字节码。对于这两 种存储区域具体实现方式在JVM规格中没有明确规定。这使得Java应用程序的存储布局必须在运行过程中确定,依赖于具体平台的实现方式。JVM是为 Java字节码定义的一种独立于具体平台的规格描述,是Java平台独立性的基础。JVM还存在一些限制和不足,有待于进一步的完善,但无论如何,JVM 的思想是成功的。

对比分析:如果把Java原程序想象成我们的C++原程序,Java原程序编译后生成的字节码就相当于C++原程序编译后的80x86的机器码(二 进制程序文件),JVM虚拟机相当于80x86计算机系统,Java解释器相当于80x86CPU。在80x86CPU上运行的是机器码,在Java解释 器上运行的是Java字节码。Java解释器相当于运行Java字节码的“CPU”,但该“CPU”不是通过硬件实现的,而是用软件实现的。Java解释 器实际上就是特定的平台下的一个应用程序。只要实现了特定平台下的解释器程序,Java字节码就能通过解释器程序在该平台下运行,这是Java跨平台的根 本。当前,并不是在所有的平台下都有相应Java解释器程序,这也是Java并不能在所有的平台下都能运行的原因,它只能在已实现了Java解释器程序的 平台下运行。

JVM到底是什么呢的更多相关文章

  1. 基础03 JVM到底在哪里?

    1.Java是编译型语言还是解释型语言? 是解释型定义: 编译型语言:把做好的源程序全部编译成二进制代码的可运行程序.然后,可直接运行这个程序. 解释型语言:把做好的源程序翻译一句,然后执行一句,直至 ...

  2. 弄懂JDK、JRE和JVM到底是什么

    首先是JDK JDK(Java Development Kit) 是 Java 语言的软件开发工具包(SDK).在JDK的安装目录下有一个jre目录,里面有两个文件夹bin和lib,在这里可以认为bi ...

  3. 弄懂JDK、JRE和JVM到底是什么 关系区别

    首先是JDKJDK(Java Development Kit) 是 Java 语言的软件开发工具包(SDK).在JDK的安装目录下有一个jre目录,里面有两个文件夹bin和lib,在这里可以认为bin ...

  4. 一个想休息的线程:JVM到底是怎么处理锁的?怎么不让我阻塞呢?

    我是一个线程,生活在JVM(Java虚拟机)中, 这一段日子过得有些无聊,整个世界似乎只有这一个人,天天忙着执行代码,想休息一下都很难. 我听说人类写的代码中有些特殊的地方,叫做临界区,比如synch ...

  5. jvm到底是什么?有什么作用?工作机制如何?

    1.jvm定义 JVM就是java虚拟机,它是一个虚构出来的计算机,可在实际的计算机上模拟各种计算机的功能.JVM有自己完善的硬件结构,例如处理器.堆栈和寄存器等,还具有相应的指令系统. 2.jvm作 ...

  6. Jvm 内存浅析 及 GC个人学习总结

    从诞生至今,20多年过去,Java至今仍是使用最为广泛的语言.这仰赖于Java提供的各种技术和特性,让开发人员能优雅的编写高效的程序.今天我们就来说说Java的一项基本但非常重要的技术内存管理 了解C ...

  7. JVM是如何分配和回收内存?有实例!

    上一篇博客我简单介绍了下如何手动计算一个Java对象到底占用多少内存?今天就想聊下这个内存JVM到底是是如何分配和回收的. Java整体来说还是一个GC比较友好的语言,无论是分代的垃圾收集,还是基于G ...

  8. 关于JVM的类型和模式

    原文出处: 摆渡者 引言 曾几何时,我也敲打过无数次这样的命令: 然而之前的我都只关心过版本号,也就是第一行的内容.今天,我们就来看看第3行输出的内容:JVM的类型和工作模式. 其实说Server和C ...

  9. jvm 漫谈 笔记

    1.Jvm到底是什么呢? Jvm其实就是模拟一台计算机,每种cpu都有自己的指令集,jvm自己设置一套指令集,这就是我满看的的字节码,然后jvm需要执行这些字节码,其实这些字节码最终直接对应到cpu的 ...

随机推荐

  1. openshift 3.11 安装部署

    openshift 3.11 安装部署 openshift安装部署 1 环境准备(所有节点) openshift 版本 v3.11 1.1 机器环境 ip cpu mem hostname OSsys ...

  2. -bash: ll: 未找到命令

    第一步将alias ll='ls -l'添加到/etc/profile # head /etc/profile# /etc/profilealias ll='ls -l'# System wide e ...

  3. Python 库整理【收藏】

    库名称简介 Chardet字符编码探测器,可以自动检测文本.网页.xml的编码. colorama主要用来给文本添加各种颜色,并且非常简单易用. Prettytable主要用于在终端或浏览器端构建格式 ...

  4. Oracle中Table函数简单应用实例

    说明 表函数可接受查询语句或游标作为输入参数,并可输出多行数据. 该函数可以平行执行,并可持续输出数据流,被称作管道式输出. 应用表函数可将数据转换分阶段处理,并省去中间结果的存储和缓冲表. 优势 1 ...

  5. [论文阅读笔记] Community aware random walk for network embedding

    [论文阅读笔记] Community aware random walk for network embedding 本文结构 解决问题 主要贡献 算法原理 参考文献 (1) 解决问题 先前许多算法都 ...

  6. SpringBoot2 集成测试组件,七种测试手段对比

    一.背景描述 在版本开发中,时间段大致的划分为:需求,开发,测试: 需求阶段:理解需求做好接口设计: 开发阶段:完成功能开发和对接: 测试上线:自测,提测,修复,上线: 实际上开发阶段两个核心的工作, ...

  7. MindSpore模型精度调优实践

    MindSpore模型精度调优实践 引论:在模型的开发过程中,精度达不到预期常常让人头疼.为了帮助用户解决模型调试调优的问题,为MindSpore量身定做了可视化调试调优组件:MindInsight. ...

  8. Json文件解析(下)

    Json文件解析(下) 代码地址:https://github.com/nlohmann/json   从STL容器转换 任何序列容器(std::array,std::vector,std::dequ ...

  9. oracle表ddl审计

    ============= 表ddl 审计============== 1.table信息 SQL> select * from test; ID CUST_CREDIT_LIMIT TIME ...

  10. 「题解」HDU-4015 Mario and Mushrooms

    本文将同步发布于: 洛谷博客: csdn: 博客园: 简书: 题目 题目链接:HDU-4015 Mario and Mushrooms.Vjudge HDU-4015. 题意简述 马里奥初始只有 \( ...