JVM是基于栈的,DVM是基于寄存器的
Jvm中基本数据类型所占用的字节空间
byte: 1,short: 2,int:4,float:4,long:8,double:8,char:2,object(对象):4
Jvm由五部分组成:一组指令集,一组寄存器,一个栈,一个无用单元收集堆(GC),一个方法区域
Java指令集
java虚拟机支持大约248个字节码,每个字节码执行一种基本的CPU运算,例如把一个整数加到寄存器,Java指令集相当于java程序的汇编语言,Java指令集中的指令包含一个单字节的操作符用于指定要执行的操作。
寄存器
寄存器用于保存机器的运行状态,与cpu中的某些专用寄存器类似,所有寄存器都是32位的,在Java虚拟机中有四种寄存器:Java程序计数器(pc),指向操作数栈顶端的指针(optop),指向当前执行方法的执行环境的指针(frame),指向当前执行方法的局部变量区第一个变量的指针(vars),Java虚拟机是栈式的,他不定义或使用寄存器来传递或接受参数,其目的是为了保证指令集的简洁性和实现的高效性
栈
栈中分为三个区域:
- 局部变量区:
每一个Java方法使用一个固定大小的局部变量集,他们按照vars寄存器的字偏移量来寻址,局部变量都是32位的,long和double占用了两个局部变量的空间,却按照第一个局部变量的索引来寻址 - 运行环境区:
在运行环境区中包含的信息可以实现动态链接,正常的方法返回与异常和错误传播。 
- 动态链接
方法的 class 文件代码在引用要调用的方法和要访问的变量时使用符号。动态链接把符号形式的方法调用翻译成实际方法调用,装载必要的类以解释还没有定义的符号,并把变量访问翻译成与这些变量运行时的存储结构相应的偏移地址 - 正常的方法返回
如果当前方法正常地结束了,在执行了一条具有正确类型的返回指令时,调用的方法会得到一个返回值。执行环境在正常返回的情况下用于恢复调用者的寄存器,并把调用者的程序计数器增加一个恰当的数值,以跳过已执行过的方法调用指令,然后在调用者的执行环境中继续执行下去 - 异常和错误传播
异常情况(Error和Exception)是Throwable类的子类,在程序中的原因有如下两点: 
- 动态链接出错,如无法找到所需的class文件
 - 运行时出错,如对一个空指针的引用程序使用了throw语句,当发生异常时,Java虚拟机会采取如下措施解决:
 
- 检查与当前方法相联系的 catch 子句表。每个 catch 子句包含其有效指令范围,能够处理异常类型,以及处理异常的代码块地址
 - 与异常相匹配的 catch 子句应该符合下面的条件:造成异常的指令在其指令范围内,发生的异常类型是其能处理的异常类型的子类型。如果找到了匹配的 catch 子句,那么系统将转移到指定的异常处理块处执行。如果没有找到异常处理块,则重复寻找匹配的catch 子句的过程,直到当前方法的所有嵌套的 catch 子句都被检查过
 - 由于虚拟机从第一个匹配的 catch 子句处继续执行,所以 catch 子句表中的顺序是很重要的。因为 Java 代码是结构化的,因此总可以把某个方法中所有的异常处理器都按序排列到一个表中,对任意可能的程序计数器的值,都可以用线性的顺序找到合适的异常处理块,以处理在该程序计数器值下发生的异常情况
 - 如果找不到匹配的 catch 子句,那么当前方法得到一个“未截获异常”的结果并返回到当前方法的调用者,好像异常刚刚在其调用者中发生一样。如果在调用者中仍然没有找到相应的异常处理块,那么这种错误传播将被继续下去。如果错误被传播到最顶层,那么系统将调用一个缺省的异常处理块。
 
- 操作数栈区:
机器指令只从操作数栈中取操作数,对它们进行操作,并把结果返回到栈中。选择栈结构的原因是:在只有少量寄存器或非通用寄存器的机器(如 Intel 486)上,也能够高效地模拟虚拟机的行为。操作数栈是 32 位的,用于给方法传递参数,并从方法接收结果,也用于支持操作的参数,并保存操作的结果
每个原始数据类型都有专门的指令对它们进行必需的操作。每个操作数在栈中需要一个存储位置,除了 long 和 double 型,它们需要两个位置。操作数只能被适用于其类型的操作符所操作。例如压入两个 int 类型的数,如果把它们当作是一个 long 类型的数则是非法的。在Sun 的虚拟机实现中,这个限制由字节码验证器强制实行。但是有少数操作(操作符 dupe 和 swap),用于对运行时数据区进行操作时是不考虑类型的 - 无用单元收集堆(GC)
Java的堆是一个运行时数据区,类的实例(对象)从中分配空间,Java语言具有无用单元收集的能力 - 方法区
每个 Java虚拟机实例都有一个方法区以及一个堆,它们是由该虚拟机实例中所有线程共享的。当虚拟机装载一个 class 文件时,它会从这个 class 文件所包含的二进制数据中解析类型信息。然后,它把这些类型信息放到方法区中。当程序运行时,虚拟机会把所有该程序在运行时创建的对象都放到堆中。当每一个新线程被创建时,它都将得到自己的 PC 寄存器(程序计数器)以及一个 Java 栈:如果线程正在执行的是一个 Java 方法(非本地方法),那么 PC 寄存器的值将总是指示下一条将被执行的指令,而其 Java 栈则总是存储该线程中 Java 方法调用的状态——包括它的局部变量,被调用时传进来的参数,它的返回值,以及运算的中间结果等。而本地方法调用的状态,则是以某种依赖于具体实现的方式存储在本地方法栈中的,也可能是在寄存器或者其他某些与特定实现相关的内存区中
Java 栈是由许多栈帧(stackframe)或者说帧(frame)组成的,一个栈帧包含一个 Java 方法调用的状态。当线程调用一个 Java 方法时,虚拟机压入一个新的栈帧到该线程的 Java 栈中当该方法返回时,这个栈帧被从 Java 栈中弹出并抛弃 
如果在同一台计算机上同时运行三个 Java 程序,则会得到三个 Java 虚拟机实例。每个 Java 程序都运行在它自己的 Java 虚拟机实例中。
在 Java 虚拟机内部有两种线程:守护线程与非守护线程,守护线程通常是由虚拟机自己使用的,比如执行垃圾收集任务的线程。但是,Java 程序也可以把它创建的任何线程标记为守护线程。而 Java 程序中的初始线程——就是开始与 main()的那个,是非守护线程。
只要还有任何非守护线程在运行,那么这个 Java 程序也在继续运行(虚拟机仍然存活)。当
该程序中所有的非守护线程都终止时,虚拟机实例将自动退出












网友评论