Java-JVM 运行时内存结构(Run-Time Data Areas)

Java 虚拟机定义了在程序执行期间使用的各类运行时数据区域。html

其中一些数据区域全部线程共享,在 Java 虚拟机(JVM)启动时建立,仅在 Java 虚拟机退出时销毁。java

还有一些数据区域是每一个线程的。线程数据区域是在线程启动时建立,线程结束时销毁。git

 

1、运行时数据区划分(JDK8)

一、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(运行时常量池,方法区的一部分)编程语言

 

2、区划分详情

2.1.The PC Register(PC 寄存器)

每一个 JVM 线程都有本身的 pc 寄存器(内存为线程私有,随着线程的建立而建立,线程的结束而销毁)。函数

在任什么时候候,每一个 JVM 线程都在执行单个方法的代码,即该线程的当前方法(字节码解释器经过改变程序计数器来选取下一条须要执行指令,从而实现代码的流程控制,分支、循环、跳转、异常处理、线程恢复等基础功能都须要依赖这个计数器来完成)。

若是该方法不是 native,则 pc 寄存器包含当前正在执行的 JVM 指令的地址(线程切换就知道上次线程执行到哪了)。

若是该方法是 native,则 pc 寄存器为 Undefined(不会 OutOfMemoryError)。

 

2.2.Java Virtual Machine Stacks(Java 虚拟机栈)

描述 Java 方法执行的内存模型。

每一个 JVM 线程都有一个私有 JVM 栈,与线程同时建立(内存为线程私有,随着线程的建立而建立,线程的结束而销毁)。

JVM 栈存储 frames (栈帧)。方法调用和返回对应压栈和出栈(栈顶的栈帧是当前正在执行的活动栈,也就是当前正在执行的方法,PC 寄存器也会指向这个地址,只有这个活动的栈帧的本地变量能够被操做数栈使用)。

因为除了压栈和出栈以外,永远不会直接操做 JVM 栈,JVM 栈的内存不须要是连续的。

JVM 规范容许 JVM 栈具备固定大小,也能够根据计算的须要动态扩展和收缩(经过 -Xss 控制)。 

如下异常与 JVM 栈有关:

若是不能够动态扩展 Java 虚拟机栈,当线程中的方法调深度用超过 Java 虚拟机栈最大深度时,会抛出 StackOverflowError 异常(出现 StackOverFlowError 时,内存空间可能还有不少)。

若是能够动态扩展 Java 虚拟机栈,当线程尝试进行扩展但可以使内存不足以实现扩展,或者可以使内存不足觉得新线程建立初始 Java 虚拟机堆栈时,会抛出 OutOfMemoryError 异常。

 

2.3.Native Method Stacks(本地方法栈)

描述本地方法运行过程的内存模型。

JVM 可使用常规栈来支持 native 方法(用 Java 编程语言之外的语言编写的方法,执行也会建立栈帧)。

没法加载 native 方法,而且自己不依赖于传统堆栈的 JVM, 不须要提供本地方法栈。若是提供,则一般在每一个线程建立时分配本地方法栈。

本地方法栈具备固定大小,也能够根据计算的须要动态扩展和收缩。

如下异常与本地方法栈有关:

若是不能够动态扩展本地方法栈,当线程中的计算须要比容许的本地方法栈更大,则会抛出 StackOverflowError 异常。

若是能够动态扩展本地方法栈,当尝试进行本地方法栈扩展,但可以使内存不足,或没有足够的内存可用于为当前前程建立初始本地方法栈,则会抛出 OutOfMemoryError 异常。

 

2.4.Heap(堆)

堆是运行时数据区,从中分配全部类实例和数组的内存(JVM 中内存最大的一块,被全部线程共享,须要注意同步问题)。

堆是在 JVM 启动时建立的。

堆中对象的存储由垃圾收集器(GC,自动存储管理系统回收,对象永远不会被显式释放。

JVM 没有特定类型的 GC,能够根据实现者的系统要求选择存储管理技术。

堆能够具备固定大小,也能够根据计算的须要进行扩展(经过 -Xmx 和 -Xms 控制)。堆的内存不须要是连续的。

如下异常状况与堆有关:

若是计算须要的堆量超过自动存储管理系统可用的堆,则会抛出 OutOfMemoryError 异常。

 

2.5.Method Area(方法区)

方法区在全部 JVM 线程之间共享。方法区是在 JVM 启动时建立的。方法区在逻辑上是堆的一部分,但可选择不实现垃圾回收。

方法区存储类结构,如运行时常量(Run-Time Constant Pool),字段和方法数据,以及方法和构造函数的代码,包括类和实例初始化以及接口初始化中使用的特殊方法

JVM 规范未规定方法区的位置或用于管理编译代码的策略。

方法区能够是固定大小的,也能够根据计算的须要进行扩展。方法区的内存不须要是连续的。

如下异常与方法区有关:

若是方法区域中的内存没法知足分配请求,会抛出 OutOfMemoryError 异常。

 

2.6.Run-Time Constant Pool(运行时常量池)

运行时常量池是方法区的一部分。

Class 文件中的常量池(constant_pool Table),用于存放编译期生成的各类字面量和符号引用,这部分在类加载后进入方法区的运行时常量池中。

在运行期间,也能够向常量池中添加新的常量。如 String 类的 intern() 方法。

每一个运行时常量池都是从 JVM 的方法区分配的

如下异常与类或接口的运行时常量池的构造有关:

在建立类或接口时,若是运行时常量池的构造须要的内存比 JVM 方法区中可用的内存多,会抛出 OutOfMemoryError 异常

 

3、直接内存(堆外内存)

不是 JVM 运行时数据区的一部分,但这部份内存也会被频繁的使用,也可能致使 OutOfMemoryError 异常。

JDK 1.4 中新加入了 NIO 类,经过调用本地方法直接分配 Java 虚拟机以外的内存,而后经过一个存储在堆中的 DirectByteBuffer 对象直接操做该内存。

避免了 Java 堆和 Native 堆来回交换数据的时间,更高效。

 

4、大概划分图(JDK8-HotSpot)


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

http://www.javashuo.com/article/p-pmycbtot-ey.html

相关文章
相关标签/搜索