java内存区域包括:程序计数器、虚拟机栈、本地方法栈、java堆、方法区。
其中在我们平常的理解中java的内存模型一方面是栈,另一方面是堆,这两个区域是与java程序运行时关系较大的区域,也是OutofMemoryError经常发生的地方。因此先从我们最先接触到的栈与堆说起。
一、java内存区域栈分为虚拟机栈与本地方法栈,均是线程私有的。
其中虚拟机栈是用于java方法执行的内存模型,在每个方法执行时会创建一个栈帧,主要存储局部变量表、操作数栈、动态链表、方法接口等信息。
其中局部变量表是我们最熟悉的结构,主要存放编译时可知的基本数据类型(boolean、byte、int、long、short、double、float、char)、对象引用、returnAddress类型。
对于虚拟栈java规范规定了两个异常:(1)线程申请的栈深度大于虚拟机允许的深度,抛出stackoverflowerror异常;
(2)如虚拟机栈可动态扩展,如果扩展时无法申请到足够的内存,则抛出OutofMemoryError。
而本地方法栈为java虚拟机使用的本地方法提供服务,该区域视虚拟机具体实现,在sun hotspot中它与虚拟机栈合二为一。同样也将抛出stackoverflowerror异常和抛出OutofMemoryError。
二、java堆
java堆是线程共享的,唯一的用途就是存放对象实例。
java堆中存放的对象实例按分代算法来看分为:新生代与老年代。
再细致点分为Eden、from survivor、to survivor等区。
从内存分配的角度看,它分为各个线程私有的缓冲区(TLAB)。
java堆逻辑上连续,物理上不一定连续。如果堆中没有足够的内存完成对象实例的存储,并且堆大小无法扩展,则抛出OutMemoryError异常。
前边针对我们熟悉的堆与栈进行了一些介绍,下面介绍被我们忽略的一些区域。
一、程序计数器
该区域相对较小,是线程私有的。它理解为程序执行的字节码的行号指示器。
如果虚拟机执行java方法则该区域记录指令地址,而当虚拟机执行native方法时,该区为空。
此区域也是java虚拟机规范中唯一一个没有规定任何OutofMemoryError的区域。
二、方法区(Non-heap)
该区域为线程共享区域,它主要用于存储已经被虚拟机加载的类信息、常量、静态变量、即时编译出后的代码等数据。
在hotspot虚拟机中该区域被称为“永久代”、应该说使用永久代来实现方法区,并通过gc统一管理该区域。
当方法区无法满足内存分配需求时,将抛出OutofMemoryError。
三、运行时常量池
该区域为方法区的一部分,用于存放class文件编译生成的各种字面量与符号引用。
该区域根据提供商实现的虚拟机的需求来实现,java虚拟机规范并未对此区域做要求。
该区域还具备动态性,运行时产生的新的常量也将存放在本区域中。如String的inten()方法
四、直接内存
该区域并非虚拟机运行时的数据区,也不是java虚拟机规范中定义的内存区域。
nio中提供一些方法,如通道(channel)、buffer等,它们可以直接使用Native函数库直接分配堆外内存。
该区域不会受到虚拟机内存的大小的限制,但是会受本机总内存的限制。也可能导致OutofMemoryError。