本文来自:曹胜欢博客专栏。转载请注明出处:http://blog.csdn.net/csh624366188

在曾经的博客里面,我们介绍了在java领域中大部分的知识点,从最基础的java最基本的语法到SSH框架。这里面应该包括了在java领域里面的大部分内容了吧。可是,那些知识点是让我们从一个应用的层面上了解了java,java程序真正底层的执行机制和一些底层虚拟机的工作我们还不了解,尽管这些内容在我们真正的开发中差点儿用不到这些底层的东西,但对于我们对java的理解会有比較大的帮助。尤其也对以后java开发中的性能优化有非常大帮助,能够使我们降低一些不是必需的内存浪费等优点。所以,从今天開始,我将和大家一起来学习一下java虚拟机的内容。从底层开一下java的执行机制。

Java虚拟机

Java虚拟机(Java Virtual Machine) 简称JVM Java虚拟机是一个想象中的机器,在实际的计算机上通过软件模拟来实现。Java虚拟机有自己想象中的硬件,如处理器、堆栈、寄存器等,还具有对应的指令系统。以下我们就来看一下这几部分比較重要的java虚拟机的结构

JVM寄存器

全部的CPU均包括用于保存系统状态和处理器所需信息的寄存器组。假设虚拟机定义义较多的寄存器,便能够从中得到很多其它的信息而不必对栈或内存进行訪问,这有利于提高运行速度。然而,假设虚拟机中的寄存器比实际CPU的寄存器多,在实现虚拟机时就会占用处理器大量的时间来用常规存储器模拟寄存器,这反而会减少虚拟机的效率。针对这样的情况,JVM仅仅设置了4个最为经常使用的寄存器。它们是:pc程序计数器,optop操作数栈顶指针 ,frame当前运行环境指针, vars指向当前运行环境中第一个局部变量的指针, 全部寄存器均为32位。pc用于记录程序的运行。optop,frame和vars用于记录指向Java栈区的指针。

JVM栈结构

作为基于栈结构的计算机,Java栈是JVM存储信息的主要方法。当JVM得到一个java字节码应用程序后,便为该代码中一个类的每个方法创建一个栈框架,以保存该方法的状态信息。每个栈框架包含下面三类信息:局部变量运行环境操作数栈 局部变量用于存储一个类的方法中所用到的局部变量。vars寄存器指向该变量表中的第一个局部变量。运行环境用于保存解释器对Java字节码进行解释过程中所需的信息。它们是:上次调用的方法、局部变量指针和操作数栈的栈顶和栈底指针。运行环境是一个运行一个方法的控制中心。比如:假设解释器要运行iadd(整数加法),首先要从frame寄存器中找到当前运行环境,而后便从运行环境中找到操作数栈,从栈顶弹出两个整数进行加法运算,最后将结果压入栈顶。  操作数栈用于存储运算所需操作数及运算的结果。

JVM碎片回收堆

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

JVM存储区

  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解释器程序的平台下执行。

Java虚拟机的体系结构图

Java虚拟机从启动到结束的生命周期,当java虚拟机启动后,在例如以下几种情况下,Java虚拟机将结束生命周期:

1.运行了System.exit()方法

2.程序正常运行结束

3.程序在运行过程中遇到了异常或错误而异常终止

4.因为操作系统出现错误而导致Java虚拟机进程终止

Java虚拟机的栈有三个区域:局部变量区、执行环境区、操作数区。

局部变量区

每一个Java方法使用一个固定大小的局部变量集。它们依照与vars寄存器的字偏移量来寻址。局部变量都是32位的。长整数和双精度浮点数占领了两个局部变量的空间,却依照第一个局部变量的索引来寻址。(比如,一个具有索引n的局部变量,假设是一个双精度浮点数,那么它实际占领了索引n和n+1所代表的存储空间)虚拟机规范并不要求在局部变量中的64位的值是64位对齐的。虚拟机提供了把局部变量中的值装载到操作数栈的指令,也提供了把操作数栈中的值写入局部变量的指令。

执行环境区

在执行环境中包括的信息用于动态链接,正常的方法返回以及异常捕捉。

操作数栈区

机器指令仅仅从操作数栈中取操作数,对它们进行操作,并把结果返回到栈中。选择栈结构的原因是:在仅仅有少量寄存器或非通用寄存器的机器(如Intel486)上,也可以高效地模拟虚拟机的行为。操作数栈是32位的。它用于给方法传递參数,并从方法接收结果,也用于支持操作的參数,并保存操作的结果。比如,iadd指令将两个整数相加。相加的两个整数应该是操作数栈顶的两个字。这两个字是由先前的指令压进堆栈的。这两个整数将从堆栈弹出、相加,并把结果压回到操作数栈中。

每一个原始数据类型都有专门的指令对它们进行必须的操作。每一个操作数在栈中须要一个存储位置,除了long和double型,它们须要两个位置。操作数仅仅能被适用于其类型的操作符所操作。比如,压入两个int类型的数,假设把它们当作是一个long类型的数则是非法的。在Sun的虚拟机实现中,这个限制由字节码验证器强制实行。可是,有少数操作(操作符dupe和swap),用于对执行时数据区进行操作时是不考虑类型的。

本地方法栈,当一个线程调用本地方法时,它就不再受到虚拟机关于结构和安全限制方面的约束,它既能够訪问虚拟机的执行期数据区,也能够使用本地处理器以及不论什么类型的栈。比如,本地栈是一个C语言的栈,那么当C程序调用C函数时,函数的參数以某种顺序被压入栈,结果则返回给调用函数。在实现Java虚拟机时,本地方法接口使用的是C语言的模型栈,那么它的本地方法栈的调度与使用则全然与C语言的栈同样。

下图能够表示出来java程序执行的一个全过程

3  Java虚拟机的执行过程

上面对虚拟机的各个部分进行了比較具体的说明,以下通过一个具体的样例来分析它的执行过程。

虚拟机通过调用某个指定类的方法main启动,传递给main一个字符串数组參数,使指定的类被装载,同一时候链接该类所使用的其他的类型,而且初始化它们。比如对于程序:

class HelloApp

{

public static void main(String[] args)

{

System.out.println("Hello World!");

for (int i = 0; i < args.length; i++ )

{

System.out.println(args[i]);

}

}

}

编译后在命令行模式下键入: java HelloApp run virtual machine

将通过调用HelloApp的方法main来启动java虚拟机,传递给main一个包括三个字符串"run"、"virtual"、"machine"的数组。如今我们略述虚拟机在运行HelloApp时可能採取的步骤。

開始试图运行类HelloApp的main方法,发现该类并没有被装载,也就是说虚拟机当前不包括该类的二进制代表,于是虚拟机使用ClassLoader试图寻找这种二进制代表。假设这个进程失败,则抛出一个异常。类被装载后同一时候在main方法被调用之前,必须对类HelloApp与其他类型进行链接然后初始化。链接包括三个阶段:检验,准备和解析。检验检查被装载的主类的符号和语义,准备则创建类或接口的静态域以及把这些域初始化为标准的默认值,解析负责检查主类对其他类或接口的符号引用,在这一步它是可选的。类的初始化是对类中声明的静态初始化函数和静态域的初始化构造方法的运行。一个类在初始化之前它的父类必须被初始化。整个步骤例如以下:

推荐阅读(内含jvm内存区域说明):

Java程序猿从笨鸟到菜鸟之(九十三)深入java虚拟机(二)——类载入器具体解释(上)

參考资料:http://www.kuqin.com/java/20080525/8907.html

Java程序猿从笨鸟到菜鸟之(九十二)深入java虚拟机(一)——java虚拟机底层结构具体解释的更多相关文章

  1. Java程序员从笨鸟到菜鸟之(二十一)java过滤器和监听器详解 【转】

     过滤器 1.Filter工作原理(执行流程) 当客户端发出Web资源的请求时,Web服务器根据应用程序配置文件设置的过滤规则进行检查,若客户请求满足过滤规则,则对客户请求/响应进行拦截,对请求头和请 ...

  2. Java程序员从笨鸟到菜鸟全部博客目录

    本文来自:曹胜欢博客专栏.转载请注明出处:http://blog.csdn.net/csh624366188 大学上了一年半,接触java也一年半了,虽然中间也有其他东西的学习,但是还是以java为主 ...

  3. 《Java程序员由笨鸟到菜鸟》

    <Java程序员由笨鸟到菜鸟> 在众多朋友的支持和鼓励下,<Java程序员由菜鸟到笨鸟>电子版终于和大家见面了.本电子书涵盖了从java基础到javaweb开放框架的大部分内容 ...

  4. Java程序员从笨鸟到菜鸟之(十三)java网络通信编程

    本文来自:曹胜欢博客专栏.转载请注明出处:http://blog.csdn.net/csh624366188 首先声明一下,刚开始学习java网络通信编程就对他有一种畏惧感,因为自己对网络一窍不通,所 ...

  5. Java程序员从笨鸟到菜鸟之(一百零二)sql注入攻击详解(三)sql注入解决办法

    sql注入攻击详解(二)sql注入过程详解 sql注入攻击详解(一)sql注入原理详解 我们了解了sql注入原理和sql注入过程,今天我们就来了解一下sql注入的解决办法.怎么来解决和防范sql注入, ...

  6. Java程序员从笨鸟到菜鸟之(五十一)细谈Hibernate(二)开发第一个hibernate基本详解

    在上篇博客中,我们介绍了<hibernate基本概念和体系结构>,也对hibernate框架有了一个初步的了解,本文我将向大家简单介绍Hibernate的核心API调用库,并讲解一下它的基 ...

  7. Java程序员从笨鸟到菜鸟之(九十六)深入java虚拟机(五)——java本地接口JNI详解

    http://blog.csdn.net/csh624366188/article/details/8063144 对于Java程序员来说,java语言的好处和优点,我想不用我说了,大家自然会说出很多 ...

  8. Java程序员从笨鸟到菜鸟之(一百)sql注入攻击详解(一)sql注入原理详解

    前段时间,在很多博客和微博中暴漏出了12306铁道部网站的一些漏洞,作为这么大的一个项目,要说有漏洞也不是没可能,但其漏洞确是一些菜鸟级程序员才会犯的错误.其实sql注入漏洞就是一个.作为一个菜鸟小程 ...

  9. java程序员从笨鸟到菜鸟之(七)一—java数据库操作

     本文来自:曹胜欢博客专栏.转载请注明出处:http://blog.csdn.net/csh624366188 数据库访问几乎每一个稍微成型的程序都要用到的知识,怎么高效的访问数据库也是我们学习的一个 ...

随机推荐

  1. Sicily-1050 深度优先搜索

    一.      题意 给出5个数和4则运算,看能不能算出目标值出来,如果算不出来就算出比目标值小的最大值.深搜:每一步选两个数做运算,然后算出的结果作为下一步的其中一个操作数.每一步选数有C(5,2) ...

  2. Flex的学习资源

    学习网站 http://www.adobe.com/cn/devnet/flex.html Adobe Flex开发人员中心 http://www.adobe.com/cn/devnet/flex/v ...

  3. IOS开发之格式化日期时间的使用 && 编程中常见问题

    今天在做一个有关时间的一些开发的时候,遇见了一写问题,反正来说既是很简单的问题,但毕竟用了我一些时间去调错误,遂记录之. 本来是想用 NSDateFormat 来转换一下服务器返回的时间,然后在客户端 ...

  4. Piggy-Bank (完全背包)

      Description Before ACM can do anything, a budget must be prepared and the necessary financial supp ...

  5. COM编程-注册DLL形式的COM服务器

    这篇文章不涉及任何的有关COM的技术的讲解,仅仅的说一下写好的DLL形式的COM怎么使用.如下图所示,我已经有了一个DLL形式的COM服务器和一个使用COM服务器的COM客户端: 现在这个DLL的CO ...

  6. C#调用C/C++动态库 封送结构体,结构体数组

    因为实验室图像处理的算法都是在OpenCV下写的,还有就是导航的算法也是用C++写的,然后界面部分要求在C#下写,所以不管是Socket通信,还是调用OpenCV的DLL模块,都设计到了C#和C++数 ...

  7. Linux 环境下 fork 函数和 exec 函数族的使用

    前言 接触 Linux 已经有几个月了,以前在网上看各路大神均表示 Windows 是最烂的开发平台,我总是不以为然,但是经过这段时间琢磨,确实觉得 Linux 开发给我带来不少的便利.下面总结一下学 ...

  8. Qt技巧:Win7下打包发布Qt程序(解释的比较清楚,把exe和dll伪装合并成一个文件)

    转自:http://www.stardrad.com/blog/qt-5%E7%A8%8B%E5%BA%8F%E5%9C%A8windows%E4%B8%8A%E7%9A%84%E5%8F%91%E5 ...

  9. perl学习(3) 列表

      列表或称为数组,和c语言中的数据类似,只是perl自己特殊的一些写法. 1.1.定义 一个列表或者数组可以包含数字,字符串,undef 值,或者任意不同类型的标量值的组合,但是这些元素的类型通常是 ...

  10. SQLite取值时的下标与创建表中字段的关系