Java虚拟机体系结构分析

下图是JAVA虚拟机的结构图:

  每个Java虚拟机都有一个类装载子系统,它根据给定的全限定名来装入类型(类或接口)。同样,每个Java虚拟机都有一个执行引擎,它负责执行那些包含在被装载类的方法中的指令。 当JAVA虚拟机运行一个程序时,它需要内存来存储许多信息,Java虚拟机把这些信息都组织到几个“运行时数据区”中,以便于管理。运行时数据区共包括五个部分(方法区、Java堆区、Java栈区、程序计数器、本地方法栈)。

1.类加载子系统

  在JAVA虚拟机中,负责查找并加载类型的那部分被称为类加载子系统。

  类加载器子系统在Java虚拟机中实现了类加载的全过程动作,这些动作必须严格按以下顺序进行:

(1)加载——主要完成3件事:

1.通过一个类的全限定名来获取定义此类的二进制字节流。

2.将这个字节流所代表的静态存储结构转化为方法区的运行时数据结构。

3.在内存中生成一个代表这个类的java.lang.Class对象,作为方法区这个类的各种数据的访问入口。

(2)连接——指向验证、准备、以及解析(可选)。

● 验证 确保Class文件的字节流中包含的的信息符合当前虚拟机的要求,并且不会危害到虚拟机自身的安全。

● 准备 为类变量分配内存,并将其初始化为默认值。

● 解析 把类型中的符号引用转换为直接引用。

(3)初始化——把类变量初始化为正确初始值。

类加载器:用来加载 Java 类到 Java 虚拟机中,实现“通过一个类的全限定名来获取描述此类的二进制字节流”动作的代码模块。

对于任意一个类,都需要由加载它的类加载器和这个类本身一同确立其在Java虚拟机中的唯一性,每一个类加载器都拥有一个独立的类名称空间。也就是说,比较两个类是否“相等”,只有在这两个类是由同一个类加载器加载的前提下才有意义。

JAVA虚拟机有两种类加载器:启动类加载器和用户自定义类加载器。前者由C++实现,是JAVA虚拟机实现的一部分;后者则是由Java程序实现,独立于虚拟机外部。

2.运行时数据区

2.1方法区(线程共享)

用于存放已被虚拟机加载的类信息、常亮、静态变量、及时编译后的代码等数据。Java虚拟机规范把方法区描述为堆的一个逻辑部分,但为了将其与Java堆区分开来,称其别名为:Non-Heap(非堆)。

对于每个装载的类型,虚拟机都会在方法区中存储以下类型信息:

  ● 这个类型的全限定名

  ● 这个类型的直接超类的全限定名(除非这个类型是java.lang.Object,它没有超类)

  ● 这个类型是类类型还是接口类型

  ● 这个类型的访问修饰符(public、abstract或final的某个子集)

  ● 任何直接超接口的全限定名的有序列表

 除了上面列出的基本类型信息外,虚拟机还得为每个被装载的类型存储以下信息:

  ● 该类型的常量池

  ● 字段信息

  ● 方法信息

  ● 除了常量以外的所有类(静态)变量

  ● 一个到类ClassLoader的引用

  ● 一个到Class类的引用

2.1.1常量池

  存放字符串常量基本类型常量(public static final)符号引用。虚拟机必须为每个被加载的类型维护一个常量池。常量池就是该类型所用常量的一个有序集合,包括直接常量和对其他类型、字段和方法的符号引用,池中的数据项就像数组一样是通过索引访问的。因为常量池存储了相应类型所用到的所有类型、字段和方法的符号引用,所以它在Java程序的动态连接中起着核心的作用。

2.1.2字段信息

对于类型中声明的每一个字段。方法区中必须保存下面的信息。除此之外,这些字段在类或者接口中的声明顺序也必须保存。

  ○ 字段名

  ○ 字段的类型

  ○ 字段的修饰符(public、private、protected、static、final、volatile、transient的某个子集)

2.1.3方法信息

 对于类型中声明的每一个方法,方法区中必须保存下面的信息。和字段一样,这些方法在类或者接口中的声明顺序也必须保存。

  ○ 方法名

  ○ 方法的返回类型(或void)

  ○ 方法参数的数量和类型(按声明顺序)

  ○ 方法的修饰符(public、private、protected、static、final、synchronized、native、abstract的某个子集)

  除了上面清单中列出的条目之外,如果某个方法不是抽象的和本地的,它还必须保存下列信息:

  ○ 方法的字节码(bytecodes)

  ○ 操作数栈和该方法的栈帧中的局部变量区的大小

  ○ 异常表

2.1.4类(静态)变量

  类变量是由所有类实例共享的,但是即使没有任何类实例,它也可以被访问。这些变量只与类有关——而非类的实例,因此它们总是作为类型信息的一部分而存储在方法区。除了在类中声明的编译时常量外,虚拟机在使用某个类之前,必须在方法区中为这些类变量分配空间。

  而编译时常量(就是那些用final声明以及用编译时已知的值初始化的类变量)则和一般的类变量处理方式不同,每个使用编译时常量的类型都会复制它的所有常量到自己的常量池中,或嵌入到它的字节码流中。作为常量池或字节码流的一部分,编译时常量保存在方法区中——就和一般的类变量一样。但是当一般的类变量作为声明它们的类型的一部分数据面保存的时候,编译时常量作为使用它们的类型的一部分而保存。

2.1.5指向ClassLoader类的引用

  每个类型被装载的时候,虚拟机必须跟踪它是由启动类装载器还是由用户自定义类装载器装载的。如果是用户自定义类装载器装载的,那么虚拟机必须在类型信息中存储对该装载器的引用。这是作为方法表中的类型数据的一部分保存的。

  虚拟机会在动态连接期间使用这个信息。当某个类型引用另一个类型的时候,虚拟机会请求装载发起引用类型的类装载器来装载被引用的类型。这个动态连接的过程,对于虚拟机分离命名空间的方式也是至关重要的。为了能够正确地执行动态连接以及维护多个命名空间,虚拟机需要在方法表中得知每个类都是由哪个类装载器装载的。

2.1.6指向Class类的引用

  对于每一个被装载的类型(不管是类还是接口),虚拟机都会相应地为它创建一个java.lang.Class类的实例,而且虚拟机还必须以某种方式把这个实例和存储在方法区中的类型数据关联起来。

  方法区使用实例详见参考一

2.2Java堆(线程共享)

Java程序在运行时创建的所有类实例对象(包括String对象)或数组都放在同一个堆中。而一个JAVA虚拟机实例中只存在一个堆空间,因此所有线程都将共享这个堆。又由于一个Java程序独占一个JAVA虚拟机实例,因而每个Java程序都有它自己的堆空间——它们不会彼此干扰。但是同一个Java程序的多个线程却共享着同一个堆空间,在这种情况下,就得考虑多线程访问对象(堆数据)的同步问题了。

JAVA 堆是垃圾收集器管理的主要区域,JAVA虚拟机有一条在堆中分配新对象的指令,却没有释放内存的指令,正如你无法用Java代码区明确释放一个对象一样。虚拟机自己负责决定如何以及何时释放不再被运行的程序引用的对象所占据的内存。通常,虚拟机把这个任务交给垃圾收集器。

关于Java堆和Java栈存储内容简单的区分记忆:凡是new出来的对象,都存在堆区;凡是引用都存在栈区★

2.3Java栈 (线程私有)

  存放基本类型的值,对象的引用(包括String引用、数组引用)和 returnAddress,在编译期间完成分配。

  每当启动一个新线程时,Java虚拟机都会为它分配一个Java栈。Java栈以帧为单位保存线程的运行状态。虚拟机只会直接对Java栈执行两 种操作:以帧为单位的压栈和出栈。

  某个线程正在执行的方法被称为该线程的当前方法,当前方法使用的栈帧称为当前帧,当前方法所属的类称为当前类,当前类的常量池称为当前常量池。在线程执行一个方法时,它会跟踪当前类和当前常量池。此外,当虚拟机遇到栈内操作指令时,它对当前帧内数据执行操作。

  每当线程调用一个Java方法时,虚拟机都会在该线程的Java栈中压入一个新帧。而这个新帧自然就成为了当前帧。在执行这个方法时,它使用这个帧来存储参数、局部变量、中间运算结果等数据。

  Java方法可以以两种方式完成。一种通过return返回的,称为正常返回;一种是通过抛出异常而异常终止的。不管以哪种方式返回,虚拟机都会将当前帧弹出Java栈然后释放掉,这样上一个方法的帧就成为当前帧了。

  Java帧上的所有数据都是此线程私有的。任何线程都不能访问另一个线程的栈数据,因此我们不需要考虑多线程情况下栈数据的访问同步问题。当一个线程调用一个方法时,方法的的局部变量保存在调用线程Java栈的帧中。只有一个线程能总是访问那些局部变量,即调用方法的线程。

  两个局部变量相加时栈帧的内存快照如下:

2.4程序计数器 (线程私有)

  对于一个运行中的Java程序而言,其中的每一个线程都有它自己的PC(程序计数器)寄存器,用来指示下一条指令的地址。分支、循环、跳转、异常处理、线程恢复等基础功能都需要依赖这个计数器来完成。它是在该线程启动时创建的,PC寄存器的大小是一个字长(因此它既能够持有一个本地指针,也能够持有一个returnAddress)。当线程执行某个Java方法时,PC寄存器的内容总是下一条将被执行指令的“地址”,这里的“地址”可以是一个本地指针,也可以是在方法字节码中相对于该方法起始指令的偏移量。如果该线程正在执行一个本地方法,那么此时PC寄存器的值是"undefined"。

2.5本地方法栈 (线程私有)

  这是 Java 调用操作系统本地库的地方,它与Java栈的区别是Java虚拟机为虚拟机执行Java方法(字节码)服务,而本地方法栈则为虚拟机执行使用到的Native方法服务。当某个线程调用一个本地方法时,它就进入了一个全新的并且不再受虚拟机限制的世界。在虚拟机规范中对本地方法栈中使用的语言、使用方式与数据结构并没有强制规定,因此具体的虚拟机可以自由实现它。

  任何本地方法接口都会使用某种本地方法栈。当线程调用Java方法时,虚拟机会创建一个新的栈帧并压入Java栈。然而当它调用的是本地方法时,虚拟机会保持Java栈不变,不再在线程的Java栈中压入新的帧,虚拟机只是简单地动态连接并直接调用指定的本地方法。

  如果某个虚拟机实现的本地方法接口是使用C连接模型的话,那么它的本地方法栈就是C栈。当C程序调用一个C函数时,其栈操作都是确定的。传递给该函数的参数以某个确定的顺序压入栈,它的返回值也以确定的方式传回调用者。同样,这就是虚拟机实现中本地方法栈的行为。

  很可能本地方法接口需要回调Java虚拟机中的Java方法,在这种情况下,该线程会保存本地方法栈的状态并进入到另一个Java栈。

  下图描绘了这样一个情景,就是当一个线程调用一个本地方法时,本地方法又回调虚拟机中的另一个Java方法。这幅图展示了JAVA虚拟机内部线程运行的全景图。一个线程可能在整个生命周期中都执行Java方法,操作它的Java栈;或者它可能毫无障碍地在Java栈和本地方法栈之间跳转。

  该线程首先调用了两个Java方法,而第二个Java方法又调用了一个本地方法,这样导致虚拟机使用了一个本地方法栈。假设这是一个C语言栈,其间有两个C函数,第一个C函数被第二个Java方法当做本地方法调用,而这个C函数又调用了第二个C函数。之后第二个C函数又通过本地方法接口回调了一个Java方法(第三个Java方法),最终这个Java方法又调用了一个Java方法(它成为图中的当前方法)。

3.执行引擎

  Java虚拟机的核心,控制装入 Java 字节码并解析;对于运行中的Java程序而言,每一个线程都是一个独立的虚拟机执行引擎的实例,从线程生命周期的开始到结束,他要么在执行字节码,要么在执行本地方法。

 

4.本地接口

  连接了本地方法栈和操作系统库。

参考:  http://www.cnblogs.com/java-my-life/archive/2012/08/01/2615221.html

http://www.codeceo.com/article/java-jvm-struct-learn.html#0-tsina-1-96459-397232819ff9a47a7b7e80a40613cfe1

《深入理解java虚拟机———jvm高级特性与最佳实践》

时间: 2024-11-05 14:50:14

Java虚拟机体系结构分析的相关文章

java虚拟机体系分析

  一.JVM的生命周期: 1)程序开始执行,他就运行,程序停止,它就结束.有几个程序在执行,就有几个虚拟机在工作.只要Java虚拟机中还有普通的线程在执行,Java虚拟机就不会停止. 2)Java虚拟机总是开始于一个main()方法,这个方法必须是公有.返回void.接受一个字符串数组.在程序执行时,你必须给Java虚拟机指明这个包含main()方法的类名. Main()方法是程序的起点,他被执行的线程初始化为程序的初始线程.程序中其他的线程都由他来启动. 3)Java中的线程分为两种:守护线

《深入理解Java虚拟机》第一部分(Java技术体系,Java虚拟机,Java技术趋势)

第一部分 走进Java 1.Java技术体系 Sun官方定义的Java技术体系包括一下几个组成部分: Java程序设语言 各种硬件平台上的Java虚拟机 Class文件格式 Java API类库 第三方Java类库 JDK是用于支持Java程序开发的最小环境,Java程序设计语言.Java虚拟机.JavaAPI类库统称为JDKJRE是支持Java程序运行的标准环境,JavaAPI类库中的JavaSEAPI子集和Java虚拟机统称为JRE 按照Java技术关注的重点业务领域来分,Java技术体系可

深入理解Java虚拟机 - Java体系

使用JAVA已经快三年了,但说来惭愧,一直以来认为Java就是Java语言本身,最多再包括一个JVM,对于整个Java的体系结构还是不甚明了,现在有时间把<深入理解Java虚拟机>这本书读了一下,也算解答了自己很多的疑惑. JAVA不仅仅是一门语言,而是由一系列软件和规范形成的技术体系,我们可以根据这些技术体系构造符合JAVA规范的虚拟机,语言,解析器等:由于JAVA的最初的设计目标是为了实现“一次编写,到处运行”的理想,整个软件界在这个理想的鼓舞下,针对不同的应用场景,设计了不同的VM以及语

Java体系介绍(深入理解Java虚拟机)

网络带来的挑战和机遇: 平台无关性.安全性和网络移动性,Java体系的这三方面共同使得Java和发展中的网络计算环境相得益彰 Java体系结构包括四个独立但相关的技术: Java程序设计语言 Java class文件格式 Java应用编程接口 Java虚拟机 1.Java虚拟机 Java虚拟机是一台抽象的计算机,其规范定义了每个Java虚拟机都必须实现的特征,但是给每个特定实现都留下了很多选择. Java虚拟机的主要任务是装在class文件并且执行其中的字节码 Java虚拟机由类装载器和执行引擎

Java 虚拟机结构分析

本博文主要介绍了JVM(Java Virtual Machine)的组成部分以及它们内部的工作机制和原理.需要注意的是,虽然平时我们用的大多是Sun(现已被Oracle收购)JDK提供的JVM,但是JVM本身是一个规范,所以可以有多种实现,除了Hotspot外,还有诸如Oracle的JRockit.IBM的J9也都是非常有名的JVM. 1. 结构 下图展示了JVM的主要结构: 可以看出,JVM主要由类加载器子系统.运行时数据区(内存空间).执行引擎以及与本地方法接口等组成.其中运行时数据区又由方

Java虚拟机结构分析

本博文主要介绍了JVM(Java Virtual Machine)的组成部分以及它们内部的工作机制和原理.需要注意的是,虽然平时我们用的大多是Sun(现已被Oracle收购)JDK提供的JVM,但是JVM本身是一个规范,所以可以有多种实现,除了Hotspot外,还有诸如Oracle的JRockit.IBM的J9也都是非常有名的JVM. 1. 结构 下图展示了JVM的主要结构: 可以看出,JVM主要由类加载器子系统.运行时数据区(内存空间).执行引擎以及与本地方法接口等组成.其中运行时数据区又由方

深入理解java虚拟机-----&gt;垃圾收集器与内存分配策略(下)

1.  前言 内存分配与回收策略 JVM堆的结构分析(新生代.老年代.永久代) 对象优先在Eden分配 大对象直接进入老年代 长期存活的对象将进入老年代 动态对象年龄判定 空间分配担保  2.  垃圾收集器与内存分配策略 Java技术体系中所提倡的自动内存管理最终可以归结为自动化地解决两个问题: 给对象分配内存; 回收分配给对象的内存. 对象的内存分配,往大方向上讲就是在堆上的分配,对象主要分配在新生代的Eden区上.少数也可能分配在老年代,取决于哪一种垃圾收集器组合,还有虚拟机中的相关内存的参

JAVA 异常体系

JAVA异常体系超类:Throwable 两个子类:Error(错误),Exception(异常) 异常体系采用的是"继承". Error:在程序中无法处理的错误,表示运行应用程序中出现严重错误. 主要是VirtualMachineError(虚拟机错误): OutOfMemoryError:内存溢出.其中内存溢出又分为 java.lang.OutOfMemoryError: Java heap spacejava.lang.OutOfMemoryError: unable to cr

《深入理解Java虚拟机:JVM高级属性与最佳实践》读书笔记(更新中)

第一章:走进Java 概述 Java技术体系 Java发展史 Java虚拟机发展史 1996年 JDK1.0,出现Sun Classic VM HotSpot VM, 它是 Sun JDK 和 OpenJDK 中所带的虚拟机,最初并不是Sun开发 Sun Mobile- Embedded VM/ Meta- Circular VM BEA JRockit/ IBM J9 VM JRockit曾号称世界上最快的java虚拟机,BEA公司发布.J9属于IBM主要扶持的虚拟机 Azul VM/ BEA