什么是JVM?通常我们的意思可能是:
对JVM规范的比较抽象的说明;
对JVM的具体实现;
在程序运行期间所生成的一个JVM实例。
JVM可以由不同的厂商来实现。由于厂商的不同必然导致JVM在实现上的一些不同,然而JVM还是可以实现跨平台的特性,这就要归功于设计JVM时的体系结构。
一个JVM的实例不光是它自己的事,还涉及到它的子系统、存储区域、数据类型和指令这些部分,它们描述了JVM的一个抽象的内部体系结构,其目的是规定实现JVM时它内部的体系结构,更重要是提供了一种方式,用于严格定义实现时的外部行为。每个JVM都有两种机制,一个是类装载子系统;另外一个叫做运行引擎。当JVM运行一个程序时,它需要内存来存储许多东西,例如,字节码、从已装载的class文件中得到的其他信息,程序创建的对象,传递给方法的参数,返回值,局部变量,以及运算的中间结果等等,JVM把这些东西都组织到几个“运行时数据区”中,但是规范对它们的描述相当抽象,这些运行时数据区的实现细节,大多数都由具体的设计者决定。
运行时数据区由以下几部分组成:
多个线程共享的有:方法区和堆
方法区:当虚拟机装载一个class文件时,它会从这个class文件包含的二进制数据中解析类型信息,然后,将这些类型信息放到方法区中。
方法区存放装载的类数据信息包括:基本信息、每个类的全限定名、每个类的直接超类的全限定名、该类是类还是接口、该类型的访问修饰符、直接超接口的全限定名的有序列表。每个已装载类的详细信息:运行时常量池、字段信息、方法信息、静态变量、到类classloader的引用、到类class的引用、方法表。
堆:存放程序运行时创建的对象。
每个线程独自拥有的有:Java栈、本地方法栈、PC寄存器(程序计数器)。
当每一个新线程被创建时,它都将得到自己的PC寄存器以及一个java栈。如果线程正在执行的是一个java方法(非本地方法),那么PC寄存器的值总是指示下一条将要被执行的命令(命令的地址),而它的java栈总是存储该线程中java方法调用的状态--包括它的局部变量、被调用时传进来的参数、它的返回值、以及运算的中间结果等等。而本地方法调用的状态,则是以某种依赖于具体实现的方式存储在本地方法栈中,也可能是在寄存器或者其他某些与特定实现相关的内存区中。当线程调用的是本地方法时,程序计数器的值不被定义。
java栈:
java栈是由许多栈帧(stack frame)或者说帧组成的。一个栈帧包含一个java方法的调用状态。当线程调用一个java方法时,虚拟机压入一个新的栈帧到该线程的java栈中;当该方法返回时,这个栈帧被从java栈中弹出并抛弃。
栈帧由3部分组成:局部变量区、操作数栈、帧数据区。局部变量区和操作数栈的大小要视对应的方法而定,它们是按字长计算的。编译器在编译时就确定了这些值并保存在class文件中,而帧数据区的大小依赖于具体的实现。
局部变量区:用于存放方法的参数和局部变量,编译器首先按声明的顺序把这些参数放入局部变量数组。局部变量区被组织为一个以字长为单位、从0开始计数的数组,指令是以索引来访问局部变量的。
操作数栈:和局部变量区一样,操作数栈也是被组织成一个以字长为单位的数组、但是和前者不同的是,它不是通过索引来访问的,而是通过标准的栈操作--压栈和出栈来访问的。虚拟机没有寄存器,所以java虚拟机的指令是从操作数栈中而不是寄存器中取得操作数的。
帧数据区:除了局部变量区和操作数栈外,java栈帧还需要一些数据来支持常量池解析、正常方法返回以及异常派发机制,这些信息都保存在java栈帧的帧数据区中。