每一个线程都会有本身私有的程序计数器(PC)。能够看做是当前线程所执行的字节码的行号指示器。
也能够理解为下一条将要执行的指令的地址或者行号。字节码解释器就是经过改变这个计数器的值来选取下一条须要执行的字节码指令,分支、 循环、 跳转、 异常处理、 线程上下文切换,线程恢复时,都要依赖PC.java
若是线程正在执行的是一个Java方法,PC值为正在执行的虚拟机字节码指令的地址程序员
若是线程正在执行的是Native方法,PC值为空(未定义)数组
VM Stack也是线程私有的区域。他是java方法执行时的字典:它里面记录了局部变量表、 操做数栈、 动态连接、 方法出口等信息。数据结构
在《java虚拟机规范》一书中对这部分的描述以下:app
栈帧( Frame)是用来存储数据和部分过程结果的数据结构,同时也被用来处理动态连接 (Dynamic Linking)、 方法返回值和异常分派( Dispatch Exception)。
栈帧随着方法调用而建立,随着方法结束而销毁——不管方法是正常完成仍是异常完成(抛出了在方法内未被捕获的异常)都算做方法结束。
栈帧的存储空间分配在 Java 虚拟机栈( §2.5.5)之中,每个栈帧都有本身的局部变量表( Local Variables, §2.6.1)、操做数栈( OperandStack, §2.6.2)和指向当前方法所属的类的运行时常量池( §2.5.5)的引用。ide
说白了,VM Stack是一个栈
,也是一块内存区域
。
因此,他是有大小的。虽然有大小,可是通常而言,各类虚拟机的实现都支持动态扩展这部份内存。函数
若是线程请求的栈深度太大,则抛出StackOverflowError
spa
若是动态扩展时没有足够的大小,则抛出OutOfMemoryError
操作系统
Java 虚拟机实现可能会使用到传统的栈(一般称之为“ C Stacks”)来支持 native 方法( 指使用 Java 之外的其余语言编写的方法)的执行,这个栈就是本地方法栈( Native MethodStack)。线程
VM Stack是为执行java方法服务的,此处的Native Method Stack是为执行本地方法服务的。
此处的本地方法指定是和具体的底层操做系统层面相关的接口调用了(这部分过高高级了,不想深究……)。
《java虚拟机规范》中没有对这部分作具体的规定。因此就由VM的实现者自由发挥了。
有的虚拟机(好比HotSpot)将VM Stack和Native Method Stack合二为一,因此VM的另外一种内存区域图就以下面所示了:
在 Java 虚拟机中,堆( Heap)是可供各条线程共享的运行时内存区域,也是供全部类实例和数组对象分配内存的区域。
如下是本人对《java虚拟机规范》一书中对Java堆的介绍的总结:
在虚拟机启动的时候就被建立
是全部线程共享的内存区域
存储了被自动内存管理系统所管理的各类对象
这些受管理的对象无需,也没法显式地被销毁
自动内存管理系统:Automatic StorageManagement System,也便是常说的”Garbage Collector(垃圾收集器)”
并未指明用什么具体的技术去实现自动内存管理系统
Java 堆的容量能够是固定大小的,也能够随着程序执行的需求动态扩展,并在不须要过多空间时自动收缩
Java 堆所使用的内存不须要保证是连续的
若是实际所需的堆超过了自动内存管理系统能提供的最大容量,那 Java 虚拟机将会抛出一个OutOfMemoryError
异常
实现者应当提供给程序员或者最终用户调节 Java 堆初始容量的手段
对于能够动态扩展和收缩 Java 堆来讲,则应当提供调节其最大、最小容量的手段
全部的对象实例以及数组都要在堆上分配
方法区是由全部线程共享的内存区域。
方法区存储的大体内容以下:
每个类的结构信息
运行时常量池( Runtime Constant Pool)
字段和方法数据
构造函数和普通方法的字节码内容
类、实例、接口初始化时用到的特殊方法
每个运行时常量池都分配在 Java 虚拟机的方法区之中,在类和接口被加载到虚拟机后,对应的运行时常量池就被建立出来。
当建立类或接口的时候,若是构造运行时常量池所须要的内存空间超过了方法区所能提供的最大值,那 Java 虚拟机将会抛出一个 OutOfMemoryError
异常。
此处的直接内存并非由JVM管理的内存。他是利用本地方法库
直接在java堆以外申请的内存区域。
好比NIO中的DirectByteBuffer
就是操做直接内存的。
直接内存的好处就是避免了在java堆和native堆直接同步数据的步骤。可是他并非由JVM来管理的。