JVM 探究(一):JVM内存模型概念模型

作为一个 Java 程序员,不了解 Java 内存模型就不能写出能够充分利用内存的代码。本文通过对 Java 内存模型的介绍,让读者能够了解 Java 的内存的分配情况,适合 Java 初学者或者对 JMM 不熟悉的同学。后面的博客会针对每个部分做更加深入的解释。

Java 内存模型

首先通过下图对于 Java 内存模型又一个整体的认识,然后针对不同的区域的作用和存储的内容做进一步的解释。

JVM 探究(一):JVM内存模型概念模型_第1张图片
Java内存模型

PC(程序计数器)

这里的 PC 不是 Personal Computer,而是 Program Counter Register,从名字就可以看出来,这是一个寄存器,用来存储需要执行的指令地址。

程序计数器(Program Counter (PC))是在电脑处理器中的一个寄存器,用来指示电脑下一步要运行的指令序列。--WikiPedia

PC 和其他 JVM 内存区域最大的区别是:

“此内存区域是唯一一个在Java虚拟机规范中没有规定任何OutOfMemoryError情况的区域。
摘录来自: 周志明. “深入理解Java虚拟机:JVM高级特性与最佳实践(第2版)”。 iBooks.

像上面的图片一样,PC 是每个线程私有的,对于 Java 方法而言,PC 中存储的是正在执行的虚拟机字节码的内存地址;对于 Native 方法来说,PC 中的值为空(Undefined)。

Java 虚拟机栈和本地方法栈

虚拟机栈

无论是在大学的 Java 编程课堂上,还是我们在学习过程编码过程,经常会出现 StackOverFlow,甚至目前最大的技术问答社区的名字也是 StackOverFlow。Java 语言中会产生栈溢出的就是这块内存区域,当你的程序中设置了超过 JVM 规定的递归深度的时候就会触发这个异常。类似 JMM 的其他内存区域,如果虚拟机栈在动态扩展的时候无法申请到足够的内存也会报OOM异常。

Java 语言中每一个方法的执行都对应着一个栈帧(Stack Frame)的创建,栈帧中存储的是局部变量、方法出口等信息,因此对于一个方法的执行而言,所能够使用到的内存是在编译期间就能够完全确定的,在运行期间不会发生变化。在栈帧中,局部变量空间成为 Slot,除了 double 和 long 占有 2 个 slot 外,其他基本数据类型和对象引用都占用 1 个 slot 空间

本地方法栈

本地方法栈和虚拟机栈最大的区别就是虚拟机栈是为执行 Java 字节码服务的,而本地方法栈是为了虚拟机使用到的 Native 方法服务的。除此之外,Java 虚拟机规范并没有针对本地方法栈的实现做具体规定。在 HotSpot 虚拟机中,本地方法栈和虚拟机栈是共用同一块内存的,不做具体区分。同样,本地方法栈也会产生 OOM 异常和 StackOverFlow 异常。

Java 堆

“The heap is the runtime data area from which memory for all class instances and arrays is allocated。” --Java虚拟机规范

Java 虚拟机规范规定所有的实例对象和数组都应该分配到 Java 堆中。
说的通俗一点就是所有 new 出来的对象和数组都会放到该区域,由于现在的收集器都采用分代收集算法,所以在 Java 堆中又分了新生代和老年代,新生代有做了详细的区分。该区域的大小可以通过 JVM 参数 -Xmx-Xms 来设置。

直接内存

在 JDK1.4 中引入了 NIO,可以通过 Native 方法直接在堆外分配内存,然后通过在堆中存储的引用来对这块内存区域做操作。注意 这块区域并不会在 -Xmx-Xms 设置的大小之内,因此在设置 JVM 参数的时候要注意考虑这块内存区域,避免设置的内存区域总额大于物理内存

方法区

Method Area 又叫 NonHeap,也是线程共有的内存区域,用来存:

  • 类信息
  • 常量
  • 静态变量
  • 字符串常量池

在 JDK1.7 中已经将字符串常量池移出永久代,在 Java8 中更是之内取消了永久代,而是使用了元空间(MetaSpace)来存储这些信息,从而永久代的大小不需要再制定,只要不超出物理内存的限制就不会产生 OOM 异常

运行时常量池

运行时常量池主要用来存储累的版本、字段、方法、接口等描述信息。常量池(Constant Pool Table)用来存储各种字面量和符号引用。String 的 intern() 方法就是在运行期间将对象放到常量池中的。此部分也会出现 OOM 异常。

你可能感兴趣的:(JVM 探究(一):JVM内存模型概念模型)