Java内存区域与内存溢出异常

参考《深入理解Java虚拟机》

运行时数据区域

  • 方法区(共享)
  • 堆(共享)
  • 虚拟机栈(线程私有)
  • 本地方法栈(线程私有)
  • 程序计数器(线程私有)

方法区
  • 用于存储已被虚拟机加载的类信息、常量、静态变量、即时编译器编译后的代码等数据
  • 不需要连续内存且可扩展也可固定大小(同堆)
  • 可以选择不实现垃圾收集
  • 方法区无法满足内存分配需求时,抛出OOM异常

运行时常量池是方法区的一部分,虚拟机可根据自己的需要实现该区域,通常存放Class文件中的符号引用和直接引用。运行时常量池与Class文件常量池相比具有动态性,即在运行期间也可将新的常量放入池中,具体参看String.intern()方法。常量池无法再申请到内存时抛出OOM异常。

  • 是Java虚拟机所管理的内存中最大的一块
  • 虚拟机启动时创建
  • 唯一目的:在此存放对象实例(包括数组)
  • GC管理的主要区域 -> 可将堆细分为新生代和老年代
  • Java堆可以处于物理上不连续的内存空间中,只需逻辑连续
  • 可扩展也可固定大小
  • 若堆中已无内存可分配且无法再扩展,将会抛出OOM异常
虚拟机栈
  • 描述的是Java方法执行的内存模型
  • 存储的的数据结构为栈帧
  • 栈帧用于存储局部变量表、操作栈、动态链接、方法出口
  • 每个栈帧在Java方法执行时创建,方法的调用过程即栈帧在虚拟机栈中入栈出栈的过程
  • 两种异常:OutOfMemoryError与StackOverflowError
  • StackOverflowError:线程请求的栈深度大于虚拟机所允许的深度
  • OutOfMemeoryEorror:动态扩展虚拟机栈时无法申请到足够的内存

局部变量表存放了编译期可知的各种基本数据类型、对象引用类型和returnAddress类型(随着jdk更新已名存实亡),其中64位长度的long和double占用两个局部变量空间(slot),其余只占用一个。局部变量表所需内存空间在编译期间已完成分配,即运行期间不会改变该表的大小。

本地方法栈
  • 与虚拟机栈提供基本相同的服务,只不过服务对象变为了本地方法
  • 虚拟机规范对本地方法栈中方法使用的语言、使用方式和数据结构没有强制规定,故虚拟机可自由实现。例如HotSpot直接将其与虚拟机栈合并
  • 与虚拟机栈相似,有OOM和SOE异常
程序计数器
  • 可以看做是当前线程所执行的字节码的行号指示器
  • 各线程之间的计数器互不影响
  • 若线程正在执行Java方法,记录的则是正在执行的虚拟机字节码指令的地址;若线程正在执行本地方法,计数器的值则为空
  • 该内存区域是唯一一个在Java虚拟机规范中没有规定任何OOM异常的区域

直接内存
  • 不在虚拟机内存区域内
  • 不会受到Java堆大小的限制,但会受到本机总内存的大小及处理器寻址空间的限制
  • 若虚拟机中内存区域总和大于物理内存限制,啧会导致直接内存出现OOM异常

你可能感兴趣的:(java)