麻绳先生

做一些记录性的工作

(no title)

Java虚拟机——内存区域与内存溢出异常(一)

运行时数据区

运行时数据区

程序计数器

每条线程都需要有一个独立的程序计数器,各条线程之间计数器互不影响,独立存储,被称为线程私有内存。

如果线程正在执行的是一个Java方法,这个计数器记录的时正在执行的虚拟机字节码指令的地址;如果正在执行的时Native方法,这个计数器值则为空(Undefined)。此内存区域是唯一一个在Java虚拟机规范中没有规定任何OutOfMemoryError情况的区域。

Java虚拟机栈

也是线程私有,它的生命周期与线程相同。虚拟机栈描述的是Java方法执行的内存模型:每个方法执行都会创建一个栈帧(Stack Frame)用于存储局部变量表、操作数栈、动态链接、方法出口等信息。常说的“栈”一般指的是虚拟机栈中的局部变量表。

局部变量表u从南方了编译期可知的各类基本数据类型、对象引用、returnAddress类型。

局部变量表所需的内存空间在编译期间完成分配,在方法运行期间不会改变大小,但所处的栈顶可能动态扩展。

如果线程请求的栈深度大于虚拟机所允许的深度,将抛出StackOverflowError异常;如果虚拟机栈动态扩展时无法申请足够的内存,就会抛出OutOfMemoryError异常。

本地方法栈

虚拟机栈为虚拟机执行Java方法服务,也就是字节码,而本地方法栈则为虚拟机使用到的Native方法服务。

Java堆

所有线程共享的一块区域。

此内存区域的唯一目的就是存放对象实例,几乎所有的对象实例都在这里分配内存。

Java堆是垃圾收集管理的主要区域,因此还被称为“GC堆”(Garbage Collected Heap)。

根据虚拟机规范的规定,Java堆可以处于物理上不连续的内存空间中,只需要逻辑连续;在实现时,既可以是固定大小的,也可以是可扩展的,通过-Xmx和-Xms控制。

方法区

线程共享区域。主要用于存储已被虚拟机加载的类信息、常量、静态变量、即时编译器编译后的代码等数据。虽然Java虚拟机将方法区描述为堆的逻辑部分,但是他还有一个名字叫做Non-Heap,目的应该是与堆进行区分。

Java虚拟机规范对方法区的限制非常宽松,除了和Java堆一样不需要连续的存储空间,还可以选择不实现垃圾回收

运行时常量池

是方法区的一部分。Class文件中除了有类的版本、字段、方法、接口等描述信息外,还有一项信息是常量池(Constant Pool Table),用于存放编译期生成的各种字面量和符号引用,这部分信息在类加载后进入方法去的运行时常量池存放。

运行时常量池相对于Class文件常量池的另外的一个重要特征就是具备动态性,java语言并不要求常量只有编译期才能产生,也就是并非预置入Class文件中常量池的内容才能进入方法区运行时常量池,运行期间也可能有新的常量放入池中,这种特性被开发人员利用较多的就是String.intern()。

直接内存

Direct Memory并不是虚拟机运行时数据区的一部分,也不是Java虚拟机规范中定义的内存区域,但是这部分内存使用频繁,也可能导致OOM异常。

NIO类引入了一种基于通道和缓冲区的IO方式,可以使用Native函数库直接分配堆外内存,然后通过一个存储在Java堆中的DirectByteBuffer对象作为这块内存的引用进行操作,这样能在一定程度提升性能,避免了在Java堆和Native堆中来回复制数据。

直接内存的分配不受Java堆大小限制,所以-Xmx等参数信息再加直接内存可能导致总和大于物理内存限制,导致OOM异常。