一、 程序计数器
记录正在执行的虚拟机字节码指令的地址(如果正在执行的是本地方法则为空)
二、 Java 虚拟机栈
每个Java方法在执行的同时会创建一个栈帧用于存储局部变量表、操作数栈、常量池引用等信息。从方法调用直至执行完成的过程,对应着一个栈帧在Java虚拟机中入栈和出栈的过程。
可以通过-Xss
这个虚拟机参数来指定每个线程的Java虚拟机内存大小,在JDK1.4中默认为256K,而在JDK1.5+默认为1M:
java -Xss2m HackTheJava
该区域可能抛出如下异常:
- 当线程请求的栈的深度超过最大值,会抛出
StackOverflowError
异常; - 栈进行动态扩展时如果无法申请到足够内存,会抛出
OutOfMemoryError
异常。
三、本地方法栈
本地方法栈与Java虚拟机栈类似,它们之间的区别只不过是本地方法栈为本地方法服务。
本地方法一般是用其它语言(C, C++或汇编语言等)编写的,并且被编译为基于本机硬件和操作系统的程序,对待这些方法需要特别处理。
四、堆
所有对象都在这里分配内存,是垃圾收集的主要区域(“GC 堆”)。
现代的垃圾收集器基本都是采用分代收集算法,其主要思想是针对不同类型的对象采取不同的垃圾回收算法。可以将堆分成两块:
- 新生代(Young Generation)
- 老年代(Old Generation)
堆不需要连续内存,并且可以动态增加其内存,增加失败会抛出OutOfMemoryError
异常。
可以通过-Xms
和-Xmx
这两个虚拟机参数来指定一个程序的堆内存大小,第一个参数设置初始值,第二个参数设置最大值。
java -Xms1M -Xmx2M HackTheJava
五、方法区
用于存放已被加载的类信息、常量、静态变量、即时编译器编译后的代码等数据。
和堆一样不需要连续的内存,并且可以动态扩展,动态扩展失败一样会抛出OutOfMemoryError
异常。
对这块区域进行垃圾回收的主要目标是对常量池的回收和对类的卸载,但是一般比较难实现。
Hotspot
虚拟机把它当成永久代来进行垃圾回收。但是很难确定永久代的大小,因为它受到很多因素影响,并且每次Full GC之后永久代的大小都会改变,所以经常会抛出OutOfMemoryError
异常。为了更容易管理方法区,从JDK1.8开始,移除永久代,并把方法区移至元空间,它位于本地内存中,而不是虚拟机内存中。
方法区是一个JVM规范,永久代和元空间都是其一种实现方式。在JDK1.8之后,原来永久代的数据被分到堆和元空间中。元空间存储类的元信息,静态变量和常量池放入堆中。
六、运行时常量池
运行时常量池是方法区的一部分。
Class 文件中的常量池(编译器生成的字面量和符号引用)会在类加载后被放入这个区域。
七、直接内存
在 JDK 1.4 中新引入了 NIO 类,它可以使用 Native 函数库直接分配堆外内存,然后通过 Java 堆里的 DirectByteBuffer 对象作为这块内存的引用进行操作。这样能在一些场景中显著提高性能,因为避免了在堆内存和堆外内存来回拷贝数据。