
程序计数器 (Program Counter Register)
程序计数器是一块较小的内存空间,它可以看作是当前线程所执行的字节码的行号指示器。如果线程执行的是Java方法,计数器记录的是正在执行的虚拟机字节码指令的地址。如果正在执行的是native方法,计数器的值为undefined
特点
- 线程私有,生命周期和线程一致
- 此区域是唯一一个在Java虚拟机规范中没有规定任何OutOfMemoryError情况的区域
虚拟机栈 (VM Stack)
虚拟机栈描述的是Java方法执行的动态内存模型。每个方法在执行时都会创建一个栈帧用于存储局部变量表、操作数栈、动态链接、方法出口等。每个方法从调用直到结束执行,就对应着一个栈帧从虚拟机栈中入栈到出栈的过程
特点
- 线程私有,生命周期和线程一致
- 会有 StackOverflowError 和 OutOfMemoryError 异常
StackOverflowError:线程请求的栈深度大于虚拟机所允许的深度
OutOfMemoryError:如果虚拟机栈可以动态扩展,而扩展时无法申请到足够的内存
本地方法栈 (Native Method Stack)
为虚拟机使用到的 Native 方法服务。虚拟机规范中并没有对本地方法实现所采用的编程语言与数据结构采取强制规定,因此不同的虚拟机可以自己实现native方法。
特点
- 线程私有,生命周期和线程一致
- 会有 StackOverflowError 和 OutOfMemoryError 异常
- Sun HotSpot虚拟机就直接将本地方法栈和Java虚拟机栈合二为一
方法区 (Method Area)
用于存储被虚拟机加载的类信息、常量、静态变量、即时编辑器编译后的代码等数据。虚拟机规范对方法区的限制十分宽松,除了和Java堆一样不需要连续的内存空间分配和可选择固定大小或可拓展内存外,方法区也可以被垃圾回收器管理和不受其管理。
特点
- 所有线程共享
- 会有 OutOfMemoryError 异常
- Java7之前,HotSpot虚拟机中方法区也被称为“永久代”,因为在物理上,方法区使用的是由虚拟机开辟的堆内存,由于和Java堆共享内存且内存空间由垃圾收集器统一分配和管理,自然的垃圾收集也拓展到方法区上。此时,Java堆中分区为年轻代(Young Generation)和老年代(Old Generation),而方法区自然被称为永久代(Permanent Generation)
- Java8中,HotSpot虚拟机改变了原有方法区的物理实现,将原本由虚拟机管理内存的方法区的内存移到了虚拟机以外的计算机本地内存,并将其称为元空间(Metaspace)。这样一来,现在的方法区实际存储在元空间,再也不用和Java堆共享内存中,“永久代”也就永久地被撤销了。
运行时常量池
运行时常量池(Runtime Constant Pool)是.class文件中每一个类或接口的常量池表(Constant Pool Table)的运行时表示形式,属于方法区的一部分。每一个运行时常量池都在Java虚拟机的方法区中分配,在加载类和接口到虚拟机后,就创建对应的运行时常量池。
作用
- 存放编译器生成的各种字面量和符号引用。当虚拟机运行时,需要从常量池获得对应的符号引用,再用类创建或运行时解析、翻译到具体的内存地址中
- 字面量(Literal)。通俗理解就是Java中的常量,比如文本字符串、声明为final的常量值等
-
符号引用(Symbolic References)。属于编译原理中的概念
- 类和接口的全限定名
- 字段的名称和描述符
- 方法的名称和描述符
堆(Heap)
堆(Java Heap)是虚拟机管理的内存空间中最大的一块,此区域存在的唯一目的就是存放对象实例,几乎所有的对象实例都会在这被分配内存。
虚拟机规范Java堆可以处于物理上不连续的内存中,只要逻辑上是连续的即可。且和栈一样,不同的虚拟机实现可以有不同的内存分配策略,Java堆的内存即可以设计成固定大小,也可以是动态拓展的
特点
- 所有线程共享
- 会出现 OutOfMemoryError 异常
直接内存
JDK1.4中新加入NIO(New Input/Output)类,引入了一种基于通道(Channel)和缓存(Buffer)的I/O方式,它可以使用 Native 函数库直接分配堆外内存,然后通过一个存储在 Java堆中的 DirectByteBuffer 对象作为这块内存的引用进行操作。可以避免在 Java堆和 Native堆中来回的数据耗时操作
特点
- 非虚拟机运行时数据区的部分
- 会出现 OutOfMemoryError 异常。如果内存区域总和大于物理内存限制从而导致动态扩展时会出现该异常,受本机内存限制
网友评论