您的位置:首页 > 编程语言 > Java开发

Java查漏系列(1)——JVM

2011-12-29 23:10 183 查看
一年多的时间没搞过java了,最近在学习hadoop,所以想把java再捡一捡,以前主要关注java的语法和api等,很少关注底层内存等,所以现在找时间把这块捡捡。

JVM,全称java virtual machine,也就是java虚拟机,望文生义的解释就是在计算机上面再虚拟出一个执行java程序的计算机。具体一点说,jvm可以被看成一个想象中的机器,在实际的计算机上通过软件模拟来实现,有自己想象中的硬件,如处理器、堆栈、寄存器等,还有自己相应的指令系统。JVM在它的生存周期中有一个明确的任务,那就是运行Java程序,因此当Java程序启动的时候,就产生JVM的一个实例;当程序运行结束的时候,该实例也跟着消失了。下面分别从JVM在系统中所处的位置、JVM的体系结构和JVM的运行过程三个方面来对JVM做一下介绍。

1.JVM在系统中的位置

首先,从宏观上看,JVM就是运行于具体操作系统上面的一个软件,如下图所示:



在整个java平台中,JVM是介于java应用程序与具体操作系统之间的一个组件,JVM在java平台中的位置如下图所示:



在Java平台的结构中, 可以看出,Java虚拟机(JVM) 处在核心的位置,是程序与底层操作系统和硬件无关的关键。它的下方是移植接口,移植接口由两部分组成:适配器和Java操作系统, 其中依赖于平台的部分称为适配器;JVM 通过移植接口在具体的平台和操作系统上实现;在JVM 的上方是Java的基本类库和扩展类库以及它们的API, 利用Java API编写的应用程序(application) 和小程序(Java applet) 可以在任何Java平台上运行而无需考虑底层平台,
就是因为有Java虚拟机(JVM)实现了程序与操作系统的分离,从而实现了Java 的平台无关性。

2.JVM体系结构

每个JVM都有两种机制,一个是装载具有合适名称的类(类或是接口),叫做Class Loader;另外的一个负责执行包含在已装载的类或接口中的指令,叫做Execution Engine。每个JVM又包括Method Area、Heap、 Stack、PC Register和Native
Method Stack这五个部分,这几个部分和类装载机制与运行引擎机制一起组成的体系结构图为:



该图参考了网上广为流传的JVM构成图,从这个图中可以看出,整个JVM分为以下四个部分:
1)Class Loader(类装载器)
类加载器的作用是加载类文件到内存,比如编写一个HelloWord.java程序,然后通过javac编译成class文件,那怎么才能加载到内存中被执行呢?Class Loader承担的就是这个责任,并不是随便建立一个.class文件就能被加载的,Class Loader加载的class文件是有格式要求的,另外,Class Loader只管加载,只要符合文件结构就加载,至于说能不能运行,则不是它负责的,那是由ExecutionEngine负责的。
2)Execution Engine(执行引擎)
执行引擎也叫做解释器(Interpreter),负责解释命令,提交操作系统执行。执行引擎处于JVM的核心位置,在Java虚拟机规范中,它的行为是由指令集所决定的。Java指令集相当于Java程序的汇编语言。Java指令集中的指令包含一个单字节的操作符,用于指定要执行的操作,还有0个或多个操作数,提供操作所需的参数或数据。许多指令没有操作数,仅由一个单字节的操作符构成。
虚拟机的内层循环的执行过程如下:

do{
	取一个操作符字节;
	根据操作符的值执行一个动作;
}while(程序未结束);


由于指令系统的简单性,使得虚拟机执行的过程十分简单,从而有利于提高执行的效率。
3)Native Interface(本地接口)
本地接口的作用是融合不同的编程语言为Java所用,它的初衷是融合C/C++程序,Java诞生的时候是C/C++横行的时候,要想立足,必须有一个聪明的、睿智的调用C/C++程序,于是就在内存中专门开辟了一块区域处理标记为native的代码,它的具体做法是Native MethodStack中登记native方法,在ExecutionEngine执行时加载nativelibraies。目前该方法使用的是越来越少了,除非是与硬件有关的应用,比如通过Java程序驱动打印机,或者Java系统管理生产设备,在企业级应用中已经比较少见,因为现在的异构领域间的通信很发达,比如可以使用Socket通信,也可以使用Web
Service等等,不多做介绍。
4)Runtime Data Area(运行时数据区)
运行时数据区是整个JVM的重点,我们所有写的程序都被加载到这里,之后才开始运行,Java生态系统如此的繁荣,得益于该区域的优良自治。其中,Method Area和Heap是基于JVM实例的,即JVM的每个实例都有一个它自己的方法域和一个堆,运行于JVM内的所有的线程都共享这些区域,当虚拟机装载类文件的时候,它解析其中的二进制数据所包含的类信息,并把它们放到方法域中;当程序运行的时候,JVM把程序初始化的所有对象置于堆上。PC
Register和Stack是基于线程的,即每个线程创建的时候,都会拥有自己的程序计数器和 Java栈,其中程序计数器中的值指向下一条即将被执行的指令,线程的Java栈则存储为该线程调用Java方法的状态。本地方法调用的状态被存储在NativeMethod Stack,该方法栈依赖于具体的实现。
由于运行时数据区是整个JVM的重点,所以下一节详细介绍。

3.JVM运行过程

java源文件经过java编译器编译生成字节码文件(.class),字节码文件要能够在JVM中执行,需要经过以下三个步骤:装载、链接、初始化。下面分别对这三个步骤进行说明:
装载:类的装载是通过类装载器来完成的,类装载器需要完成的功能是定义一个java类,即把java字节码转换成JVM中的java.lang.class类的对象。所有的java类在JVM中的表现形式都是java.lang.class类的对象。
链接:java类的链接指的是将java类的二进制代码合并到JVM的运行状态之中的过程。在链接之前,这个类必须被成功加载。类的链接包括验证、准备和解析等几个步骤。验证用来确保java类的二进制表示在结构上是完全正确的,如果验证过程出现错误的话,会抛出java.lang.VerifyError错误。准备过程是创建java类中的静态域,并将这些域设为默认值,准备过程并不会执行代码。在一个java类中会包含对其它类或接口的形式引用,包括它的父类、所实现的接口、方法的形式参数和返回值的java类等,解析的过程就是确保这些被引用的类能被正确的找到,解析的过程可能会导致其它的java类被加载。
初始化:当一个java类第一次被真正使用到的时候,JVM会进行该类的初始化操作,初始化过程的主要操作是执行静态代码块和初始化静态域。在一个类被初始化之前,它的直接父类也需要被初始化。
下面通过一个例子来说明JVM的运行过程:

class HelloApp 
{
	public static int x = 10;
	public static void main(String[] args) 
	{
		System.out.println(x);
	}
	static{
		x = 30;
	}
}


其执行流程如下:



开始试图执行类HelloApp的main方法,发现该类并没有被装载,也就是说虚拟机当前不包含该类的二进制代表,于是虚拟机使用 ClassLoader试图寻找这样的二进制代表。如果这个进程失败,则抛出一个异常。类被装载后同时在main方法被调用之前,必须对类 HelloApp与其它类型进行链接然后初始化。以上程序中,静态域为x,静态代码块部分也是给x赋值,初始化完成后,x的值为30。

下一节重点介绍一下JVM中的Runtime Data Area这一区域。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: