Java 虚拟机定义了在程序执行期间使用的各类运行时数据区域。html
其中一些数据区域全部线程共享,在 Java 虚拟机(JVM)启动时建立,仅在 Java 虚拟机退出时销毁。java
还有一些数据区域是每一个线程的。线程数据区域是在线程启动时建立,线程结束时销毁。git
一、The pc Register(PC 寄存器、程序计数器)github
二、Java Virtual Machine Stacks(Java 虚拟机栈、Java 栈)编程
三、Native Method Stacks(本地方法栈,C栈)数组
四、Heap(堆)oracle
五、Method Area(方法区,JDK8 中的实现叫元数据区(本地内存中),JDK7 中的实现叫永久代(JVM中))jvm
六、Run-Time Constant Pool(运行时常量池,方法区的一部分)编程语言
每一个 JVM 线程都有本身的 pc 寄存器(内存为线程私有,随着线程的建立而建立,线程的结束而销毁)。函数
在任什么时候候,每一个 JVM 线程都在执行单个方法的代码,即该线程的当前方法(字节码解释器经过改变程序计数器来选取下一条须要执行指令,从而实现代码的流程控制,分支、循环、跳转、异常处理、线程恢复等基础功能都须要依赖这个计数器来完成)。
若是该方法不是 native,则 pc 寄存器包含当前正在执行的 JVM 指令的地址(线程切换就知道上次线程执行到哪了)。
若是该方法是 native,则 pc 寄存器为 Undefined(不会 OutOfMemoryError)。
描述 Java 方法执行的内存模型。
每一个 JVM 线程都有一个私有 JVM 栈,与线程同时建立(内存为线程私有,随着线程的建立而建立,线程的结束而销毁)。
JVM 栈存储 frames (栈帧)。方法调用和返回对应压栈和出栈(栈顶的栈帧是当前正在执行的活动栈,也就是当前正在执行的方法,PC 寄存器也会指向这个地址,只有这个活动的栈帧的本地变量能够被操做数栈使用)。
因为除了压栈和出栈以外,永远不会直接操做 JVM 栈,JVM 栈的内存不须要是连续的。
JVM 规范容许 JVM 栈具备固定大小,也能够根据计算的须要动态扩展和收缩(经过 -Xss 控制)。
如下异常与 JVM 栈有关:
若是不能够动态扩展 Java 虚拟机栈,当线程中的方法调深度用超过 Java 虚拟机栈最大深度时,会抛出 StackOverflowError 异常(出现 StackOverFlowError 时,内存空间可能还有不少)。
若是能够动态扩展 Java 虚拟机栈,当线程尝试进行扩展但可以使内存不足以实现扩展,或者可以使内存不足觉得新线程建立初始 Java 虚拟机堆栈时,会抛出 OutOfMemoryError 异常。
描述本地方法运行过程的内存模型。
JVM 可使用常规栈来支持 native 方法(用 Java 编程语言之外的语言编写的方法,执行也会建立栈帧)。
没法加载 native 方法,而且自己不依赖于传统堆栈的 JVM, 不须要提供本地方法栈。若是提供,则一般在每一个线程建立时分配本地方法栈。
本地方法栈具备固定大小,也能够根据计算的须要动态扩展和收缩。
如下异常与本地方法栈有关:
若是不能够动态扩展本地方法栈,当线程中的计算须要比容许的本地方法栈更大,则会抛出 StackOverflowError 异常。
若是能够动态扩展本地方法栈,当尝试进行本地方法栈扩展,但可以使内存不足,或没有足够的内存可用于为当前前程建立初始本地方法栈,则会抛出 OutOfMemoryError 异常。
堆是运行时数据区,从中分配全部类实例和数组的内存(JVM 中内存最大的一块,被全部线程共享,须要注意同步问题)。
堆是在 JVM 启动时建立的。
堆中对象的存储由垃圾收集器(GC,自动存储管理系统)回收,对象永远不会被显式释放。
JVM 没有特定类型的 GC,能够根据实现者的系统要求选择存储管理技术。
堆能够具备固定大小,也能够根据计算的须要进行扩展(经过 -Xmx 和 -Xms 控制)。堆的内存不须要是连续的。
如下异常状况与堆有关:
若是计算须要的堆量超过自动存储管理系统可用的堆,则会抛出 OutOfMemoryError 异常。
方法区在全部 JVM 线程之间共享。方法区是在 JVM 启动时建立的。方法区在逻辑上是堆的一部分,但可选择不实现垃圾回收。
方法区存储类结构,如运行时常量(Run-Time Constant Pool),字段和方法数据,以及方法和构造函数的代码,包括类和实例初始化以及接口初始化中使用的特殊方法。
JVM 规范未规定方法区的位置或用于管理编译代码的策略。
方法区能够是固定大小的,也能够根据计算的须要进行扩展。方法区的内存不须要是连续的。
如下异常与方法区有关:
若是方法区域中的内存没法知足分配请求,会抛出 OutOfMemoryError 异常。
运行时常量池是方法区的一部分。
Class 文件中的常量池(constant_pool Table),用于存放编译期生成的各类字面量和符号引用,这部分在类加载后进入方法区的运行时常量池中。
在运行期间,也能够向常量池中添加新的常量。如 String 类的 intern() 方法。
每一个运行时常量池都是从 JVM 的方法区中分配的。
如下异常与类或接口的运行时常量池的构造有关:
在建立类或接口时,若是运行时常量池的构造须要的内存比 JVM 方法区中可用的内存多,会抛出 OutOfMemoryError 异常。
不是 JVM 运行时数据区的一部分,但这部份内存也会被频繁的使用,也可能致使 OutOfMemoryError 异常。
JDK 1.4 中新加入了 NIO 类,经过调用本地方法直接分配 Java 虚拟机以外的内存,而后经过一个存储在堆中的 DirectByteBuffer 对象直接操做该内存。
避免了 Java 堆和 Native 堆来回交换数据的时间,更高效。
https://docs.oracle.com/javase/specs/jvms/se8/html/jvms-2.html#jvms-2.5
http://www.hollischuang.com/archives/2509
https://github.com/doocs/jvm/blob/master/docs/01-jvm-memory-structure.md