java虚拟机在运行时,讲内存划分为若干不同的数据区域,如下图所示:
- 程序计数器 当前线程所执行的字节码的信号指示器。 在虚拟机的概念模型中,字节码解释器,就是通过改变这个计数器的值,来选择下一条需要执行的字节码指令。分支、循环、跳转、异常处理、线程恢复等基础功能。 为了线程切换后,能恢复到正确的执行位置,每个线程都独立拥有一个程序计数器。 唯一的一个在Java虚拟机规范中,没有规定任何OutOfMemoryError情况的区域。
- 虚拟机栈 Java方法执行的内存模型。 每个方法执行时,都会创建一个栈帧,用于存储:局部变量表、操作数栈、动态链接、方法出口等信息。 局部变量表:基本数据类型、对象引用(起始地址、代表对象的句柄、对象的相关位置)、returnAddress。局部变量表所需的空间,在编译阶段就完全确定。 Java虚拟机在这个区域规定了两种异常:StackOverFlowError、OutOfMemoryError。
- 本地方法栈 Native方法执行的内存模型。 Java虚拟机在这个区域规定了两种异常:StackOverFlowError、OutOfMemoryError。
- 堆 所有的实例对象以及数组都要在堆上分配。但是随着JIT编译器的发展与逃逸分析技术逐渐成熟,栈上分配、标量替换优化技术将会导致一些微妙的变化发生,所有的对象都分配在堆上也渐渐变得不是那么“绝对”了。 线程共享。 Java堆可以物理上不连续,只要逻辑上连续即可。 异常:OutOfMemoryError
- 方法区 用于存储已被虚拟机加载的类信息、常量、静态变量、即时编译器编译后的代码等数据。 线程共享。内存回收目标:常量池的回收、类型的卸载。 异常:OutOfMemoryError 运行时常量池:用于存放编译期生成的各种字面量和符号引用。运行期间也可能有新的常量加入,比如String类的intern()方法。
- 直接内存 并不是虚拟机运行时数据区的一部分,也不是Java虚拟机规范中定义的内存区域。 NIO类,可以使用Native函数库直接分配堆外内存,然后通过一个存储在Java堆中的DirectByteBuffer对象作为这块内存的引用进行操作。可以显著提升性能。避免了在Java堆和Native堆中来回复制数据。 分配虚拟机内存时,需注意。各个内存区域总和,不能大于物理内存限制。
注:参考《深入理解Java虚拟机》(第2版)