Java虚拟机(Java Virtual Machine),简称JVM。当咱们提及Java虚拟机时,可能指的是以下三种不一样的东西:html
Java虚拟机抽象规范仅仅是一个概念,在《The Java Virtual Machine Specification》中有详细的描述。java
该规范的实现,可能来自多个提供商,并存在于多个平台上,它或者是所有由软件实现,或者是以硬件和软件相结合的方式来实现。JVM的实现有不少,广为使用的主要有三个(因为Sun和BEA被Oracle收购,故HotSpot、JRockit已都为Oracle全部):数组
当运行一个Java程序的时候,也就在运行一个Java虚拟机实例。注意,咱们所说的Java平台无关性是指class文件的平台无关性,JVM是和平台相关的,不一样操做系统对应不一样的JVM。架构
HotSpot虚拟机是官方的、最经常使用的JVM规范实现,这里以HotSpot虚拟机为例。oracle
如图,JVM能够分为三部分:类加载子系统、运行时数据区、执行引擎。spa
(图片来源:Java Garbage Collection Introduction)操作系统
更详细的结构:.net
(图片来源:The JVM Architecture Explained)线程
(图片来源:Jave SE Platform at a Glance)orm
JVM规范定义了若干种程序运行时使用到的运行时数据区:
分为两种,一种随虚拟机的启动和退出而建立和销毁(方法区、堆),一种随线程的开始和结束而建立和销毁(虚拟机栈、本地方法栈、程序计数器):
程序计数器(Program Counter Register)是一块较小的内存空间,它能够看作是当前线程所执行的字节码的信号指示器。若线程正在执行的是一个Java方法,则程序计数器保存的是当前所在线程下一条要执行的字节码指令的地址;若正在执行的是Native方法,则计数器值为空(undefined)。
线程私有,每一个JVM线程都有本身的程序寄存器。
此内存区域是惟一一个在Java虚拟机规范中没有规定任何OutOfMemoryError状况的区域。
Java虚拟机栈(Java Virtual Machine Stack)即线程运行栈,描述的是Java方法执行的内存模型:每一个方法被执行时会同时建立一个栈帧(Stack Frame)用于局部变量表(存编译期可知的基本数据类型、引用类型、returnAddress)、操做数栈、动态链表、方法出口信息等。每个方法被调用直至执行完成的过程就对应着一个栈帧在虚拟机栈中从入栈到出栈的过程。
线程私有,每个JVM线程都有本身的java虚拟机栈,这个栈与线程同时建立,它的生命周期与线程相同。
JVM stack 能够实现成固定大小,也能够实现成可动态扩展的。若实现成固定大小,则每一条线程的JVM Stack容量应该在线程建立时就指定了大小,JVM实现应该提供调节JVM Stack初始容量的手段;若实现成可动态扩展的方式,则应该提供调节最大、最小容量的手段。
能建立的栈的数量(即线程的数量)受所在机器物理内存的限制,但实际上OS对一个进程能使用的内存大小(Windows 2GB)是有限制的,故能建立栈的数量受OS的这个限制。换句话说,JVM并无支持调节各栈能用的总内存而是只提供了对一个栈大小的调节。
异常(二者实际上有重叠):
StackOverflowError:纵向没法分配即没法分配新的栈帧时 抛出此异常。
OutOfMemoryError:横线没法分配即没法创建新的线程(在建立新线程时没有足够内存来建立对应的虚拟机栈时 或者 JVM Stack能够动态扩展但在尝试扩展时没法申请到足够的内存来完成扩展,也就是超过了OS对一个进程能建立的线程数的限制时)时 抛出此异常。
Java虚拟机可能会使用到传统的栈来支持native方法(使用Java语言之外的其它语言编写的方法)的执行,这个栈就是本地方法栈(Native Method Stack)。
线程私有。
本地方法栈发挥的做用域Java虚拟机栈类似。有的虚拟机(如HotSpot)不对二者加以区分,合二为一。
方法区(Method Area)用于存储已被虚拟机加载的类信息(版本、字段、方法、接口等描述信息)、常量、静态变量、即时编译后的代码、运行时常量池等。
各线程共享。
方法区的容量能够实现成固定大小的,也能够实现成可动态扩展的,并在不须要过多空间时候自动收缩。
方法区能够处于物理上不连续的内存空间中,只要逻辑连续便可。
OutOfMemoryError异常: 方法区的内存空间不能知足内存分配请求,且没法扩展时抛出此异常。
运行时常量池:存放编译器生成的各类字面量和符号引用
是方法区的一部分。Class文件编译后生成的各类字面量和符号引用放在文件的常量池中,这部份内容在类加载后进入方法区的运行时常量池。
与Class文件常量池相比,运行时常量池具有动态性:不要求常量只有在编译期才能产生,即并不是预置入Class文件中常量池的内容才能进入运行时常量池,运行期间也可将新常量放入运行时常量池,如String的intern()方法(见Java小记-MarchOn)。
OutOfMemoryError异常:当建立类和接口时,若是构造运行时常量池所需的内存空间超过了方法区所能提供的最大内存空间后就会抛出此异常。
注:方法区/永久代/元空间:
方法区常又被称为永久代(PermGen space)、元空间(Metaspace),它们并不等价,区别是:方法区为JVM的规范,永久代、元空间分别是方法区在HotSpot中的一种实现;对于其余类型的虚拟机实现,如 JRockit(BEA)、J9(IBM),其并无永久代、元空间这些概念,其只要不触碰到进程可用内存上限便可。
Java堆是几乎全部对象(有例外,如类的Class对象置于方法区)实例分配内存的区域。堆在虚拟机启动的时候被建立,堆中储存了各类对象,这些对象被自动内存管理系统(Automatic Storage Management System,也便是常说的“Garbage Collector(垃圾回收器)”)所管理。这些对象无需、也没法显示地被销毁。
各线程共享。
堆的容量能够实现成固定大小的,也能够实现成可动态扩展的,并在不须要过多空间时候自动收缩。
Java堆能够处于物理上不连续的内存空间中,只要逻辑连续便可。
Java 堆异常(OutOfMemoryError):若在堆中没有足够内存完成实例分配,而且也没法扩展时,抛出此异常。
3.六、本地直接内存(不是运行时数据区)
本地直接内存(Direct Memory)不是虚拟机运行时数据区的一部分,也不是Java虚拟机规范定义的内存区域。但也可被使用,并且也可能出现OutOfMemoryError异常。其不受堆大小的限制,但受到本机进程可用内存的限制。
上述各区域内存大小的配置可参看 HotSpot JVM各区域内存分配参数设置-MarchOn
[1]http://chenzhou123520.iteye.com/blog/1585224,(其即参考自《深刻理解Java虚拟机——JVM高级特性与最佳实践》)