jvm的内存结构

jvm的内存结构如图所示


1.程序计数器

  • 程序计数器是一块较小的内存空间,它可以看做是当前线程执行的字节码写的行号指示器。他是线程私有的,按照我的理解就是,它相当于马路上的路标,当程序执行的时候,他会获取相应的指令,让代码运行下去,程序计数器是java虚拟机中唯一没有 OutOfMemoryError情况的区域

2.Java虚拟机栈

  • Java虚拟机栈和程序计数器一样是线程私有的,他的生命周期和线程相同,当运行每个方法的时候都会创建一个栈帧,这里面存储局部变量表,操作数栈,动态链接,方法出口等信息。
  • 局部变量表就是储存的基本数据类型(\boolean,byte,char,short,int,float,long,double),对象引用等等,long和double会占用2个局部变量的空间,其余的数据类型都只占用1个,方法运行期间局部变量表的大小固定
  • Java 虚拟机栈规定了两种异常,线程请求栈的深度大于当前虚拟机所允许的深度,会抛出StackOverflowError异常(比如递归的时候)还有就是当虚拟机栈扩展时无法申请到足够的内存会有OutOfMemoryError异常。

3.本地方法栈

  • 本地方法栈和Java虚拟机栈所发挥的作用非常相似,他们之间的区别就是Java虚拟机栈执行的Java方法,本地方法栈执行的是native方法,本地方法栈和Java虚拟机栈一样都会有StackOverflowError和OutOfMemoryError异常

4.Java堆

  • Java堆是虚拟机内存中最大的一块,他是线程共享的,唯一的作用就是存放对象的实例,几乎所有的对象实例都在堆里面分配内存,
  • Java 堆是垃圾收集器管理的主要区域,
  • Java 堆可以细分为:新生代和老年代,再详细就分为:Eden(伊甸园)空间,From Survivor和To Survivor(幸存者)空间等,这样分配主要目的是为了更好地回收内存
  • Java堆可以处于物理上不连续的空间,只要逻辑上连续即可,Java堆可以通过-Xmx和-Xms来控制大小当堆没有内存完成实例分配的时候,会抛出OutOfMemoryError异常

5.方法区

  • 方法区(永久代)也是线程共享的内存区域,他用于储存一杯虚拟机加载的类的信息,常量,静态变量,即时编译器编译后的代码等数据。
  • 方法区的大小可以通过-XX:MaxPermSize设置上限,当无法满足内存分配需求时会抛出OutOfMemoryError异常
  • 方法区不需要连续的内存,可以选择固定大小,可扩展,还能选择不实现垃圾收集

6.运行时常量池

  • 运行时常量池是方法区的一部分,它用于存储编译器生成的字面量(常量和未翻译的引用
    )和符号引用(一组符号来描述所引用的目标)也会有OutOfMemoryError异常

7.直接内存

  • 直接内存(Direct Memory)并不是虚拟机运行时数据区的一部分,也不是Java虚拟机规范中定义的内存区域。但是这部分内存也被频繁地使用,而且会导致OutOfMemoryError异常。
  • 在NIO中,它可以使用Native函数库直接分配堆外内存,然后通过一个存储在Java堆中的DirectByteBuffer对象作为这块内存的引用进行操作。这样做显著提高了性能,避免在Java堆和Native堆中来回复制数据。
  • 虽然这部分空间不会受到Java堆大小的限制,但是,因为是内存空间,所以会受到本机总内存大小以及处理器寻址空间的限制。
  • 所以在配置虚拟机参数时,不能忽略直接内存,避免使各个内存区域总和大于物理内存限制。不然会导致动态扩展时出现OutOfMemoryError异常。